Skip to content

Commit 546d77d

Browse files
committed
Plumbing and renaming bits
1 parent c8793da commit 546d77d

File tree

5 files changed

+210
-104
lines changed

5 files changed

+210
-104
lines changed

powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDBProvider.java

-93
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package software.amazon.lambda.powertools.parameters;
2+
3+
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
4+
import software.amazon.awssdk.core.SdkSystemSetting;
5+
import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient;
6+
import software.amazon.awssdk.regions.Region;
7+
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
8+
import software.amazon.awssdk.services.dynamodb.model.*;
9+
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
10+
import software.amazon.lambda.powertools.parameters.transform.TransformationManager;
11+
12+
import java.util.Collections;
13+
import java.util.Map;
14+
import java.util.stream.Collectors;
15+
16+
/**
17+
* Implements a {@link ParamProvider} on top of DynamoDB. The schema of the table
18+
* is described in the Python powertools documentation.
19+
*
20+
* @see <a href="https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/parameters/#dynamodbprovider">Python DynamoDB provider</a>
21+
*
22+
*/
23+
public class DynamoDbProvider extends BaseProvider {
24+
25+
private final DynamoDbClient client;
26+
private final String tableName;
27+
28+
public DynamoDbProvider(CacheManager cacheManager, String tableName) {
29+
this(cacheManager, DynamoDbClient.builder()
30+
.httpClientBuilder(UrlConnectionHttpClient.builder())
31+
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
32+
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())))
33+
.build(),
34+
tableName
35+
);
36+
37+
}
38+
39+
DynamoDbProvider(CacheManager cacheManager, DynamoDbClient client, String tableName) {
40+
super(cacheManager);
41+
this.client = client;
42+
this.tableName = tableName;
43+
}
44+
45+
/**
46+
* Return a single value from the DynamoDB parameter provider.
47+
*
48+
* @param key key of the parameter
49+
* @return The value, if it exists, null if it doesn't. Throws if the row exists but doesn't match the schema.
50+
*/
51+
@Override
52+
protected String getValue(String key) {
53+
GetItemResponse resp = client.getItem(GetItemRequest.builder()
54+
.tableName(tableName)
55+
.key(Collections.singletonMap("id", AttributeValue.fromS(key)))
56+
.attributesToGet("val")
57+
.build());
58+
59+
// If we have an item at the key, we should be able to get a 'val' out of it. If not it's
60+
// exceptional.
61+
// If we don't have an item at the key, we should return null.
62+
if (resp.hasItem() && !resp.item().values().isEmpty()) {
63+
return resp.item().get("val").s();
64+
}
65+
66+
return null;
67+
}
68+
69+
/**
70+
* Returns multiple values from the DynamoDB parameter provider.
71+
*
72+
* @param path Parameter store path
73+
* @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.
74+
*/
75+
@Override
76+
protected Map<String, String> getMultipleValues(String path) {
77+
78+
QueryResponse resp = client.query(QueryRequest.builder()
79+
.tableName(tableName)
80+
.keyConditionExpression("id = :v_id")
81+
.expressionAttributeValues(Collections.singletonMap(":v_id", AttributeValue.fromS(path)))
82+
.build());
83+
84+
return resp
85+
.items()
86+
.stream()
87+
.collect(
88+
Collectors.toMap(
89+
(i) -> i.get("sk").s(),
90+
(i) -> i.get("val").s()));
91+
92+
93+
}
94+
95+
/**
96+
* Create a builder that can be used to configure and create a {@link DynamoDbProvider}.
97+
*
98+
* @return a new instance of {@link DynamoDbProvider.Builder}
99+
*/
100+
public static DynamoDbProvider.Builder builder() {
101+
return new DynamoDbProvider.Builder();
102+
}
103+
104+
static class Builder {
105+
private DynamoDbClient client;
106+
private String table;
107+
private CacheManager cacheManager;
108+
private TransformationManager transformationManager;
109+
110+
/**
111+
* Create a {@link DynamoDbProvider} instance.
112+
*
113+
* @return a {@link DynamoDbProvider}
114+
*/
115+
public DynamoDbProvider build() {
116+
if (cacheManager == null) {
117+
throw new IllegalStateException("No CacheManager provided; please provide one");
118+
}
119+
if (table == null) {
120+
throw new IllegalStateException("No DynamoDB table name provided; please provide one");
121+
}
122+
DynamoDbProvider provider;
123+
if (client != null) {
124+
provider = new DynamoDbProvider(cacheManager, client, table);
125+
} else {
126+
provider = new DynamoDbProvider(cacheManager, table);
127+
}
128+
if (transformationManager != null) {
129+
provider.setTransformationManager(transformationManager);
130+
}
131+
return provider;
132+
}
133+
134+
/**
135+
* Set custom {@link DynamoDbClient} to pass to the {@link DynamoDbClient}. <br/>
136+
* Use it if you want to customize the region or any other part of the client.
137+
*
138+
* @param client Custom client
139+
* @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>)
140+
*/
141+
public DynamoDbProvider.Builder withClient(DynamoDbClient client) {
142+
this.client = client;
143+
return this;
144+
}
145+
146+
/**
147+
* <b>Mandatory</b>. Provide a CacheManager to the {@link DynamoDbProvider}
148+
*
149+
* @param cacheManager the manager that will handle the cache of parameters
150+
* @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>)
151+
*/
152+
public DynamoDbProvider.Builder withCacheManager(CacheManager cacheManager) {
153+
this.cacheManager = cacheManager;
154+
return this;
155+
}
156+
157+
/**
158+
* <b>Mandatory</b>. Provide a DynamoDB table to the {@link DynamoDbProvider}
159+
*
160+
* @param table the table that parameters will be retrieved from.
161+
* @return the builder to chain calls (eg. <pre>builder.withTable().build()</pre>)
162+
*/
163+
public DynamoDbProvider.Builder withTable(String table) {
164+
this.table = table;
165+
return this;
166+
}
167+
168+
/**
169+
* Provide a transformationManager to the {@link DynamoDbProvider}
170+
*
171+
* @param transformationManager the manager that will handle transformation of parameters
172+
* @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>)
173+
*/
174+
public DynamoDbProvider.Builder withTransformationManager(TransformationManager transformationManager) {
175+
this.transformationManager = transformationManager;
176+
return this;
177+
}
178+
}
179+
}

powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java

+22-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
1717
import software.amazon.awssdk.services.ssm.SsmClient;
18+
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
1819
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
1920
import software.amazon.lambda.powertools.parameters.transform.TransformationManager;
2021

@@ -36,8 +37,8 @@ public final class ParamManager {
3637

3738
/**
3839
* Get a concrete implementation of {@link BaseProvider}.<br/>
39-
* You can specify {@link SecretsProvider} or {@link SSMProvider} or create your custom provider
40-
* by extending {@link BaseProvider} if you need to integrate with a different parameter store.
40+
* You can specify {@link SecretsProvider}, {@link SSMProvider}, {@link DynamoDbProvider}, or create your
41+
* custom provider by extending {@link BaseProvider} if you need to integrate with a different parameter store.
4142
* @return a {@link SecretsProvider}
4243
*/
4344
public static <T extends BaseProvider> T getProvider(Class<T> providerClass) {
@@ -65,6 +66,12 @@ public static SSMProvider getSsmProvider() {
6566
return getProvider(SSMProvider.class);
6667
}
6768

69+
/**
70+
* Get a {@link DynamoDbProvider} with default {@link DynamoDbClient} <br/>
71+
* If you need to customize the region, or other part of the client, use
72+
*/
73+
public static DynamoDbProvider getDynamodbProvider() { return getProvider(DynamoDbProvider.class); }
74+
6875
/**
6976
* Get a {@link SecretsProvider} with your custom {@link SecretsManagerClient}.<br/>
7077
* Use this to configure region or other part of the client. Use {@link ParamManager#getSsmProvider()} if you don't need this customization.
@@ -91,6 +98,19 @@ public static SSMProvider getSsmProvider(SsmClient client) {
9198
.build());
9299
}
93100

101+
/**
102+
* Get a {@link DynamoDbProvider} with your custom {@link DynamoDbClient}.<br/>
103+
* Use this to configure region or other part of the client. Use {@link ParamManager#getDynamodbProvider()} if you don't need this customization.
104+
* @return a {@link DynamoDbProvider}
105+
*/
106+
public static DynamoDbProvider getDynamoDbProvider(DynamoDbClient client) {
107+
return (DynamoDbProvider) providers.computeIfAbsent(DynamoDbProvider.class, (k) -> DynamoDbProvider.builder()
108+
.withClient(client)
109+
.withCacheManager(cacheManager)
110+
.withTransformationManager(transformationManager)
111+
.build());
112+
}
113+
94114
public static CacheManager getCacheManager() {
95115
return cacheManager;
96116
}
+6-6
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
* E2E tests running in the cloud CI.
2323
*/
2424
@Disabled
25-
public class DynamoDBProviderE2ETest {
25+
public class DynamoDbProviderE2ETest {
2626

2727
final String ParamsTestTable = "ddb-params-test";
2828
final String MultiparamsTestTable = "ddb-multiparams-test";
2929
private final DynamoDbClient ddbClient;
3030

31-
public DynamoDBProviderE2ETest() {
31+
public DynamoDbProviderE2ETest() {
3232
// Create a DDB client to inject test data into our test tables
3333
ddbClient = DynamoDbClient.builder()
3434
.httpClientBuilder(UrlConnectionHttpClient.builder())
@@ -50,7 +50,7 @@ public void TestGetValue() {
5050
.build());
5151

5252
// Act
53-
DynamoDBProvider provider = makeProvider(ParamsTestTable);
53+
DynamoDbProvider provider = makeProvider(ParamsTestTable);
5454
String value = provider.getValue("test_param");
5555

5656
// Assert
@@ -80,7 +80,7 @@ public void TestGetValues() {
8080
.build());
8181

8282
// Act
83-
DynamoDBProvider provider = makeProvider(MultiparamsTestTable);
83+
DynamoDbProvider provider = makeProvider(MultiparamsTestTable);
8484
Map<String, String> values = provider.getMultipleValues("test_param");
8585

8686
// Assert
@@ -89,8 +89,8 @@ public void TestGetValues() {
8989
assertThat(values.get("test_param_part_2")).isEqualTo("the_value_is_still_hello!");
9090
}
9191

92-
private DynamoDBProvider makeProvider(String tableName) {
93-
return new DynamoDBProvider(new CacheManager(), DynamoDbClient.builder()
92+
private DynamoDbProvider makeProvider(String tableName) {
93+
return new DynamoDbProvider(new CacheManager(), DynamoDbClient.builder()
9494
.httpClientBuilder(UrlConnectionHttpClient.builder()).build(),
9595
tableName);
9696
}
+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import static org.assertj.core.api.Assertions.assertThat;
1919
import static org.mockito.MockitoAnnotations.openMocks;
2020

21-
public class DynamoDBProviderTest {
21+
public class DynamoDbProviderTest {
2222

2323
@Mock
2424
DynamoDbClient client;
@@ -30,14 +30,14 @@ public class DynamoDBProviderTest {
3030
ArgumentCaptor<QueryRequest> queryRequestCaptor;
3131

3232

33-
private DynamoDBProvider provider;
33+
private DynamoDbProvider provider;
3434
private final String tableName = "ddb-test-table";
3535

3636
@BeforeEach
3737
public void init() {
3838
openMocks(this);
3939
CacheManager cacheManager = new CacheManager();
40-
provider = new DynamoDBProvider(cacheManager, client, tableName);
40+
provider = new DynamoDbProvider(cacheManager, client, tableName);
4141
}
4242

4343

0 commit comments

Comments
 (0)