Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@
@{argLine}
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-exports=java.xml.crypto/com.sun.org.slf4j.internal=ALL-UNNAMED
Comment thread
scottgerring marked this conversation as resolved.
Outdated
</argLine>
</configuration>
</plugin>
Expand Down
9 changes: 9 additions & 0 deletions powertools-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,13 @@
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package software.amazon.lambda.powertools.core.internal;

import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;

import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv;

public class UserAgentConfigurer {
Comment thread
scottgerring marked this conversation as resolved.
Outdated

public static final String NA = "NA";
public static final String VERSION_KEY = "powertools.version";
public static final String PT_FEATURE_VARIABLE = "${PT_FEATURE}";
public static final String PT_EXEC_ENV_VARIABLE = "${PT_EXEC_ENV}";
public static final String VERSION_PROPERTIES_FILENAME = "version.properties";
public static final String AWS_EXECUTION_ENV = "AWS_EXECUTION_ENV";
private static final Logger LOG = LoggerFactory.getLogger(UserAgentConfigurer.class);
private static final String NO_OP = "no-op";
private static String PT_VERSION = getProjectVersion();
private static String USER_AGENT_PATTERN = "PT/" + PT_FEATURE_VARIABLE + "/" + PT_VERSION + " PTEnv/" + PT_EXEC_ENV_VARIABLE;

/**
* Retrieves the project version from the version.properties file
*
* @return the project version
*/
static String getProjectVersion() {
Comment thread
scottgerring marked this conversation as resolved.
return getVersionFromProperties(VERSION_PROPERTIES_FILENAME, VERSION_KEY);
}


/**
* Retrieves the project version from a properties file.
* The file should be in the resources folder.
* The version is retrieved from the property with the given key.
*
* @param propertyFileName the name of the properties file
* @param versionKey the key of the property that contains the version
* @return the version of the project as configured in the given properties file
*/
static String getVersionFromProperties(String propertyFileName, String versionKey) {
Comment thread
scottgerring marked this conversation as resolved.

URL propertiesFileURI = Thread.currentThread().getContextClassLoader().getResource(propertyFileName);
if (propertiesFileURI != null) {
try (FileInputStream fis = new FileInputStream(propertiesFileURI.getPath())) {
Properties properties = new Properties();
properties.load(fis);
String version = properties.getProperty(versionKey);
if (version != null && !version.isEmpty()) {
return version;
}
} catch (IOException e) {
LOG.warn("Unable to read {} file. Using default version.", propertyFileName);
LOG.debug("Exception:", e);
}
}
return NA;
}

/**
* Retrieves the user agent string for the Powertools for AWS Lambda.
* It follows the pattern PT/{PT_FEATURE}/{PT_VERSION} PTEnv/{PT_EXEC_ENV}
* The version of the project is automatically retrieved.
* The PT_EXEC_ENV is automatically retrieved from the AWS_EXECUTION_ENV environment variable.
* If it AWS_EXECUTION_ENV is not set, PT_EXEC_ENV defaults to "NA"
*
* @param ptFeature a custom feature to be added to the user agent string (e.g. idempotency).
* If null or empty, the default PT_FEATURE is used.
* The default PT_FEATURE is "no-op".
* @return the user agent string
*/
public static String getUserAgent(String ptFeature) {

String awsExecutionEnv = getenv(AWS_EXECUTION_ENV);
Comment thread
scottgerring marked this conversation as resolved.
String ptExecEnv = awsExecutionEnv != null ? awsExecutionEnv : NA;
String userAgent = USER_AGENT_PATTERN.replace(PT_EXEC_ENV_VARIABLE, ptExecEnv);

if (ptFeature == null || ptFeature.isEmpty()) {
ptFeature = NO_OP;
}
return userAgent
.replace(PT_FEATURE_VARIABLE, ptFeature)
.replace(PT_EXEC_ENV_VARIABLE, ptExecEnv);
}

}
1 change: 1 addition & 0 deletions powertools-core/src/main/resources/version.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
powertools.version=${project.version}
Comment thread
scottgerring marked this conversation as resolved.
Outdated
Comment thread
jeromevdl marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package software.amazon.lambda.powertools.core.internal;

import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import java.io.File;
import java.util.regex.Pattern;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mockStatic;
import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv;
import static software.amazon.lambda.powertools.core.internal.UserAgentConfigurer.VERSION_KEY;
import static software.amazon.lambda.powertools.core.internal.UserAgentConfigurer.getVersionFromProperties;
import static software.amazon.lambda.powertools.core.internal.UserAgentConfigurer.VERSION_PROPERTIES_FILENAME;
import static software.amazon.lambda.powertools.core.internal.UserAgentConfigurer.AWS_EXECUTION_ENV;



public class UserAgentConfigurerTest {
Comment thread
scottgerring marked this conversation as resolved.
Outdated

private static final String SEM_VER_PATTERN = "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$";
private static final String VERSION = UserAgentConfigurer.getProjectVersion();


@Test
public void testGetVersion() {

assertThat(VERSION).isNotNull();
assertThat(VERSION).isNotEmpty();
assertThat(Pattern.matches(SEM_VER_PATTERN, VERSION)).isTrue();
}

@Test
public void testGetVersionFromProperties_WrongKey() {
String version = getVersionFromProperties(VERSION_PROPERTIES_FILENAME, "some invalid key");

assertThat(version).isNotNull();
assertThat(version).isEqualTo("NA");
}

@Test
public void testGetVersionFromProperties_FileNotExist() {
String version = getVersionFromProperties("some file", VERSION_KEY);

assertThat(version).isNotNull();
assertThat(version).isEqualTo("NA");
}

@Test
public void testGetVersionFromProperties_InvalidFile() {
File f = new File(Thread.currentThread().getContextClassLoader().getResource("unreadable.properties").getPath());
f.setReadable(false);

String version = getVersionFromProperties("unreadable.properties", VERSION_KEY);

assertThat(version).isEqualTo("NA");
}

@Test
public void testGetVersionFromProperties_EmptyVersion() {
String version = getVersionFromProperties("test.properties", VERSION_KEY);

assertThat(version).isEqualTo("NA");
}

@Test
public void testGetUserAgent() {
String userAgent = UserAgentConfigurer.getUserAgent("test-feature");

assertThat(userAgent).isNotNull();
assertThat(userAgent).isEqualTo("PT/test-feature/" + VERSION + " PTEnv/NA");

}

@Test
public void testGetUserAgent_NoFeature() {
String userAgent = UserAgentConfigurer.getUserAgent("");

assertThat(userAgent).isNotNull();
assertThat(userAgent).isEqualTo("PT/no-op/" + VERSION + " PTEnv/NA");
}

@Test
public void testGetUserAgent_NullFeature() {
String userAgent = UserAgentConfigurer.getUserAgent(null);

assertThat(userAgent).isNotNull();
assertThat(userAgent).isEqualTo("PT/no-op/" + VERSION + " PTEnv/NA");
}

@Test
public void testGetUserAgent_SetAWSExecutionEnv() {
try (MockedStatic<SystemWrapper> mockedSystemWrapper = mockStatic(SystemWrapper.class)) {
mockedSystemWrapper.when(() -> getenv(AWS_EXECUTION_ENV)).thenReturn("AWS_Lambda_java8");
String userAgent = UserAgentConfigurer.getUserAgent("test-feature");

assertThat(userAgent).isNotNull();
assertThat(userAgent).isEqualTo("PT/test-feature/" + VERSION + " PTEnv/AWS_Lambda_java8");
}
}

}
1 change: 1 addition & 0 deletions powertools-core/src/test/resources/test.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
powertools.version=
1 change: 1 addition & 0 deletions powertools-core/src/test/resources/unreadable.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
powertools.version=${project.version}
Comment thread
jeromevdl marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
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.DynamoDbClientBuilder;
import software.amazon.awssdk.services.dynamodb.model.*;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.lambda.powertools.core.internal.UserAgentConfigurer;
import software.amazon.lambda.powertools.idempotency.Constants;
import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException;
import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException;
Expand All @@ -47,6 +50,7 @@
public class DynamoDBPersistenceStore extends BasePersistenceStore implements PersistenceStore {

private static final Logger LOG = LoggerFactory.getLogger(DynamoDBPersistenceStore.class);
public static final String IDEMPOTENCY = "idempotency";

private final String tableName;
private final String keyAttr;
Expand Down Expand Up @@ -90,13 +94,14 @@ private DynamoDBPersistenceStore(String tableName,
if (idempotencyDisabledEnv == null || idempotencyDisabledEnv.equalsIgnoreCase("false")) {
DynamoDbClientBuilder ddbBuilder = DynamoDbClient.builder()
.httpClient(UrlConnectionHttpClient.builder().build())
.overrideConfiguration(ClientOverrideConfiguration.builder().putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, UserAgentConfigurer.getUserAgent(IDEMPOTENCY)).build())
.region(Region.of(System.getenv(AWS_REGION_ENV)));

// AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start
// when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set
// fall back to the default provider chain if the mode is anything other than on-demand.
String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE);
if (initializationType != null && initializationType.equals(ON_DEMAND)) {
if (initializationType != null && initializationType.equals(ON_DEMAND)) {
Comment thread
scottgerring marked this conversation as resolved.
Outdated
ddbBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
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;
Expand All @@ -10,6 +12,7 @@
import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationResponse;
import software.amazon.awssdk.services.appconfigdata.model.StartConfigurationSessionRequest;
import software.amazon.lambda.powertools.core.internal.LambdaConstants;
import software.amazon.lambda.powertools.core.internal.UserAgentConfigurer;
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
import software.amazon.lambda.powertools.parameters.transform.TransformationManager;

Expand Down Expand Up @@ -146,13 +149,15 @@ public AppConfigProvider build() {
if (client == null) {
AppConfigDataClientBuilder appConfigDataClientBuilder = AppConfigDataClient.builder()
.httpClientBuilder(UrlConnectionHttpClient.builder())
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())));
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())))
.overrideConfiguration(ClientOverrideConfiguration.builder()
.putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, UserAgentConfigurer.getUserAgent(PARAMETERS)).build());

// AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start
// when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set
// fall back to the default provider chain if the mode is anything other than on-demand.
String initializationType = System.getenv().get(AWS_LAMBDA_INITIALIZATION_TYPE);
if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) {
if (initializationType != null && initializationType.equals(LambdaConstants.ON_DEMAND)) {
appConfigDataClientBuilder.credentialsProvider(EnvironmentVariableCredentialsProvider.create());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
*/
@NotThreadSafe
public abstract class BaseProvider implements ParamProvider {
public static final String PARAMETERS = "parameters";

protected final CacheManager cacheManager;
private TransformationManager transformationManager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
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;
Expand All @@ -12,6 +14,7 @@
import software.amazon.awssdk.services.dynamodb.model.QueryRequest;
import software.amazon.awssdk.services.dynamodb.model.QueryResponse;
import software.amazon.lambda.powertools.core.internal.LambdaConstants;
import software.amazon.lambda.powertools.core.internal.UserAgentConfigurer;
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException;
import software.amazon.lambda.powertools.parameters.transform.TransformationManager;
Expand Down Expand Up @@ -192,7 +195,8 @@ public DynamoDbProvider.Builder withTransformationManager(TransformationManager
private static DynamoDbClient createClient() {
DynamoDbClientBuilder dynamoDbClientBuilder = DynamoDbClient.builder()
.httpClientBuilder(UrlConnectionHttpClient.builder())
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())));
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())))
.overrideConfiguration(ClientOverrideConfiguration.builder().putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, UserAgentConfigurer.getUserAgent(PARAMETERS)).build());

// AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start
// when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
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;
Expand All @@ -24,6 +26,7 @@
import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.lambda.powertools.core.internal.LambdaConstants;
import software.amazon.lambda.powertools.core.internal.UserAgentConfigurer;
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
import software.amazon.lambda.powertools.parameters.transform.TransformationManager;
import software.amazon.lambda.powertools.parameters.transform.Transformer;
Expand Down Expand Up @@ -285,7 +288,9 @@ public SSMProvider.Builder withClient(SsmClient client) {
private static SsmClient createClient() {
SsmClientBuilder ssmClientBuilder = SsmClient.builder()
.httpClientBuilder(UrlConnectionHttpClient.builder())
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())));
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())))
.overrideConfiguration(ClientOverrideConfiguration.builder().putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, UserAgentConfigurer.getUserAgent(PARAMETERS)).build());
;

// AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start
// when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@

import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
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.SecretsManagerClientBuilder;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest;
import software.amazon.lambda.powertools.core.internal.LambdaConstants;
import software.amazon.lambda.powertools.core.internal.UserAgentConfigurer;
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
import software.amazon.lambda.powertools.parameters.transform.TransformationManager;
import software.amazon.lambda.powertools.parameters.transform.Transformer;
Expand Down Expand Up @@ -193,7 +196,8 @@ public Builder withClient(SecretsManagerClient client) {
private static SecretsManagerClient createClient() {
SecretsManagerClientBuilder secretsManagerClientBuilder = SecretsManagerClient.builder()
.httpClientBuilder(UrlConnectionHttpClient.builder())
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())));
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())))
.overrideConfiguration(ClientOverrideConfiguration.builder().putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, UserAgentConfigurer.getUserAgent(PARAMETERS)).build());

// AWS_LAMBDA_INITIALIZATION_TYPE has two values on-demand and snap-start
// when using snap-start mode, the env var creds provider isn't used and causes a fatal error if set
Expand Down
Loading