Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Dynamo DB configuration for AWS SDK 2.x #424

Merged
merged 3 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public void buildModule(ModuleBuilder builder) throws IOException, CleanExceptio
builder.appendDependencyToModule(Constants.APP_SERVICE, dependency);
builder.setupFromTemplate("driven-adapter/" + typePath);
builder.appendToProperties("aws.dynamodb").put("endpoint", "http://localhost:8000");
builder.appendToProperties("aws.dynamodb").put("threads", "10");
new ObjectMapperFactory().buildModule(builder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,30 @@ import org.springframework.context.annotation.Profile
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient

import java.net.URI

@Configuration
open class DynamoDBConfig {
@Bean
@Profile("local")
open fun amazonDynamoDB(
@Value("\${aws.dynamodb.endpoint}") endpoint: String?
@Value("\${aws.dynamodb.endpoint}") endpoint: String
): DynamoDbAsyncClient {
return DynamoDbAsyncClient.builder()
.credentialsProvider(ProfileCredentialsProvider.create("default"))
.endpointOverride(URI.create(endpoint))
.build();
.credentialsProvider(ProfileCredentialsProvider.create("default"))
.endpointOverride(URI.create(endpoint))
.build();
}

@Bean
@Profile("dev", "cer", "pdn")
open fun amazonDynamoDB(): DynamoDbAsyncClient {
return DynamoDbAsyncClient.builder().build()
}
open fun amazonDynamoDBAsync(): DynamoDbAsyncClient {
return DynamoDbAsyncClient.builder().build()
}

@Bean
fun getDynamoDbEnhancedAsyncClient(client: DynamoDbAsyncClient): DynamoDbEnhancedAsyncClient {
return DynamoDbEnhancedAsyncClient.builder().dynamoDbClient(client).build()
}
open fun getDynamoDbEnhancedAsyncClient(client: DynamoDbAsyncClient): DynamoDbEnhancedAsyncClient {
return DynamoDbEnhancedAsyncClient.builder().dynamoDbClient(client).build()
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"driven-adapter/dynamo-db-reactive/config/dynamodb-config.java.mustache": "infrastructure/driven-adapters/dynamo-db/src/main/{{language}}/{{packagePath}}/dynamodb/config/DynamoDBConfig.java",
"driven-adapter/dynamo-db-reactive/config/dynamodb-config.unit.test.java.mustache": "infrastructure/driven-adapters/dynamo-db/src/test/{{language}}/{{packagePath}}/dynamodb/config/DynamoDBConfigTest.java",
"driven-adapter/dynamo-db-reactive/helper/adapter-operations.java.mustache": "infrastructure/driven-adapters/dynamo-db/src/main/{{language}}/{{packagePath}}/dynamodb/helper/TemplateAdapterOperations.java",
"driven-adapter/dynamo-db-reactive/helper/adapter-operations.unit.test.java.mustache": "infrastructure/driven-adapters/dynamo-db/src/test/{{language}}/{{packagePath}}/dynamodb/helper/TemplateAdapterOperations.java",
"driven-adapter/dynamo-db-reactive/helper/adapter-operations.unit.test.java.mustache": "infrastructure/driven-adapters/dynamo-db/src/test/{{language}}/{{packagePath}}/dynamodb/helper/TemplateAdapterOperationsTest.java",
"driven-adapter/dynamo-db-reactive/dynamo-db-template-adapter.java.mustache": "infrastructure/driven-adapters/dynamo-db/src/main/{{language}}/{{packagePath}}/dynamodb/DynamoDBTemplateAdapter.java",
"driven-adapter/dynamo-db-reactive/modelEntity.java.mustache": "infrastructure/driven-adapters/dynamo-db/src/main/{{language}}/{{packagePath}}/dynamodb/ModelEntity.java",
"driven-adapter/dynamo-db-reactive/build.gradle.mustache": "infrastructure/driven-adapters/dynamo-db/build.gradle"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,39 @@ package {{package}}.dynamodb
import {{package}}.dynamodb.helper.TemplateAdapterOperations
import org.reactivecommons.utils.ObjectMapper
import org.springframework.stereotype.Repository
import reactor.core.publisher.Mono
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient
import software.amazon.awssdk.enhanced.dynamodb.Key
import software.amazon.awssdk.enhanced.dynamodb.model.QueryConditional
import software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest
import java.util.function.Function


@Repository
class DynamoDBTemplateAdapter(connectionFactory: DynamoDbEnhancedAsyncClient, mapper: ObjectMapper) :
TemplateAdapterOperations<Any?, String?, ModelEntity?>(
connectionFactory,
mapper,
{ d ->
open class DynamoDBTemplateAdapter(connectionFactory: DynamoDbEnhancedAsyncClient, mapper: ObjectMapper) :
TemplateAdapterOperations<Any /*domain model*/, String, ModelEntity /*adapter model*/>(
connectionFactory, mapper,
Function { d: ModelEntity ->
mapper.map(
d,
Any::class.java
)},
"table_name"
)
Any::class.java /*domain model*/
)
}, "table_name", "secondary_index" /*index is optional*/
) /*Gateway from domain*/ {
fun getEntityBySomeKeys(partitionKey: String, sortKey: String): Mono<List<Any>> {
val queryExpression = generateQueryExpression(partitionKey, sortKey)
return query(queryExpression)
}

fun getEntityBySomeKeysByIndex(partitionKey: String, sortKey: String): Mono<List<Any>> {
val queryExpression = generateQueryExpression(partitionKey, sortKey)
return queryByIndex(queryExpression, "secondary_index" /*index is optional if you define in constructor*/)
}

private fun generateQueryExpression(partitionKey: String, sortKey: String): QueryEnhancedRequest {
return QueryEnhancedRequest.builder()
.queryConditional(QueryConditional.keyEqualTo(Key.builder().partitionValue(partitionKey).build()))
.queryConditional(QueryConditional.sortGreaterThanOrEqualTo(Key.builder().sortValue(sortKey).build()))
.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,69 +1,80 @@
package {{package}}.dynamodb.helper;

import org.reactivecommons.utils.ObjectMapper;

import java.lang.reflect.ParameterizedType;

import java.util.List;
import java.util.function.Function;

import reactor.core.publisher.Mono;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncIndex;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncTable;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.Page;
import software.amazon.awssdk.enhanced.dynamodb.model.PagePublisher;
import software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.function.Function;

public abstract class TemplateAdapterOperations<E, K, V> {
private final Class<V> dataClass;
private final Function<V, E> toEntityFn;
protected ObjectMapper mapper;
private final DynamoDbAsyncTable<V> customerTable;
private final DynamoDbAsyncIndex<V> customerTableByIndex;

protected TemplateAdapterOperations(DynamoDbEnhancedAsyncClient dynamoDbEnhancedAsyncClient, ObjectMapper mapper, Function<V, E> toEntityFn, String tableName, String... index) {
private final DynamoDbAsyncTable<V> table;
private final DynamoDbAsyncIndex<V> tableByIndex;

@SuppressWarnings("unchecked")
protected TemplateAdapterOperations(DynamoDbEnhancedAsyncClient dynamoDbEnhancedAsyncClient,
ObjectMapper mapper,
Function<V, E> toEntityFn,
String tableName,
String... index) {
this.toEntityFn = toEntityFn;
this.mapper = mapper;
ParameterizedType genericSuperclass = (ParameterizedType) this.getClass().getGenericSuperclass();
this.dataClass = (Class<V>) genericSuperclass.getActualTypeArguments()[2];
customerTable = dynamoDbEnhancedAsyncClient.table(tableName, TableSchema.fromBean(dataClass));
customerTableByIndex = index.length > 0 ? customerTable.index(index[0]) : null;
table = dynamoDbEnhancedAsyncClient.table(tableName, TableSchema.fromBean(dataClass));
tableByIndex = index.length > 0 ? table.index(index[0]) : null;
}

public Mono<Void> save(E model) {
return Mono.fromFuture(customerTable.putItem(toEntity(model)));
public Mono<E> save(E model) {
return Mono.fromFuture(table.putItem(toEntity(model))).thenReturn(model);
}

public Mono<E> getById(K id) {
return Mono.fromFuture(customerTable.getItem(Key.builder().partitionValue(AttributeValue.builder().s((String) id).build()).build())).map(this::toModel);
return Mono.fromFuture(table.getItem(Key.builder()
.partitionValue(AttributeValue.builder().s((String) id).build())
.build()))
.map(this::toModel);
}

public Mono<E> delete(E model) {
return Mono.fromFuture(customerTable.deleteItem(toEntity(model))).map(this::toModel);
return Mono.fromFuture(table.deleteItem(toEntity(model))).map(this::toModel);
}

public Mono<List<E>> query(QueryEnhancedRequest queryExpression) {
PagePublisher<V> pagePublisher = customerTable.query(queryExpression);
PagePublisher<V> pagePublisher = table.query(queryExpression);
return listOfModel(pagePublisher);
}


public Mono<List<E>> queryByIndex(QueryEnhancedRequest queryExpression, String... index) {
DynamoDbAsyncIndex<V> queryIndex = index.length > 0 ? customerTable.index(index[0]) : customerTableByIndex;
DynamoDbAsyncIndex<V> queryIndex = index.length > 0 ? table.index(index[0]) : tableByIndex;

SdkPublisher<Page<V>> pagePublisher = queryIndex.query(queryExpression);
return listOfModel(pagePublisher);
}

/**
* @return Mono<List < E>>
* @implNote Bancolombia does not suggest the Scan function for DynamoDB tables due to the low performance resulting from the design of the database engine (Key value). Optimize the query using Query, GetItem or BatchGetItem functions, and if necessary, consider the Single-Table Design pattern for DynamoDB.
* @implNote Bancolombia does not suggest the Scan function for DynamoDB tables due to the low performance resulting
* from the design of the database engine (Key value). Optimize the query using Query, GetItem or BatchGetItem
* functions, and if necessary, consider the Single-Table Design pattern for DynamoDB.
* @deprecated
*/
@Deprecated(forRemoval = true)
public Mono<List<E>> scan() {
PagePublisher<V> pagePublisher = customerTable.scan();
PagePublisher<V> pagePublisher = table.scan();
return listOfModel(pagePublisher);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,62 +1,95 @@
package {{package}}.dynamodb.helper

import org.reactivecommons.utils.ObjectMapper
import java.lang.reflect.ParameterizedType
import java.util.function.Function
import reactor.core.publisher.Mono
import software.amazon.awssdk.enhanced.dynamodb.*
import software.amazon.awssdk.core.async.SdkPublisher
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncIndex
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncTable
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient
import software.amazon.awssdk.enhanced.dynamodb.Key
import software.amazon.awssdk.enhanced.dynamodb.TableSchema
import software.amazon.awssdk.enhanced.dynamodb.model.Page
import software.amazon.awssdk.enhanced.dynamodb.model.PagePublisher
import software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest
import software.amazon.awssdk.services.dynamodb.model.AttributeValue
import java.util.stream.Collectors
import java.lang.reflect.ParameterizedType
import java.util.function.Function


abstract class TemplateAdapterOperations<E, K, V> protected constructor(
dynamoDbEnhancedAsyncClient: DynamoDbEnhancedAsyncClient,
private var mapper: ObjectMapper,
private val toEntityFn: Function<V, E>,
tableName: String
dynamoDbEnhancedAsyncClient: DynamoDbEnhancedAsyncClient,
private var mapper: ObjectMapper,
private val toEntityFn: Function<V, E>,
tableName: String,
vararg index: String
) {
private val dataClass: Class<V>
private var customerTable: DynamoDbAsyncTable<V>

private val table: DynamoDbAsyncTable<V>
private val tableByIndex: DynamoDbAsyncIndex<V>?

init {
val genericSuperclass = this.javaClass.genericSuperclass as ParameterizedType
dataClass = genericSuperclass.actualTypeArguments[2] as Class<V>
customerTable = dynamoDbEnhancedAsyncClient.table(tableName, TableSchema.fromBean(dataClass))

this.dataClass = genericSuperclass.actualTypeArguments[2] as Class<V>
table = dynamoDbEnhancedAsyncClient.table(tableName, TableSchema.fromBean(dataClass))
tableByIndex = if (index.isNotEmpty()) table.index(index[0]) else null
}

fun save(model: E): Mono<Void> {
return Mono.fromFuture(customerTable.putItem(toEntity(model)))
fun save(model: E & Any): Mono<E> {
return Mono.fromFuture(table.putItem(toEntity(model))).thenReturn(model)
}

fun getById(id: K): Mono<E> {
return Mono.fromFuture(customerTable.getItem(Key.builder().partitionValue(AttributeValue.builder().s(id.toString()).build()).build())).map(this::toModel)
return Mono.fromFuture(
table.getItem(
Key.builder()
.partitionValue(AttributeValue.builder().s(id as String).build())
.build()
)
)
.map { data: V -> this.toModel(data) }
}

fun delete(model: E): Mono<E> {
return Mono.fromFuture(customerTable.deleteItem(toEntity(model))).map(this::toModel)
return Mono.fromFuture(table.deleteItem(toEntity(model))).map { data: V ->
this.toModel(
data
)
}
}

fun query(queryExpression: QueryEnhancedRequest): Mono<List<E>> {
var pagePublisher: PagePublisher<V> = customerTable.query(queryExpression)
val pagePublisher = table.query(queryExpression)
return listOfModel(pagePublisher)
}

fun queryByIndex(queryExpression: QueryEnhancedRequest, vararg index: String?): Mono<List<E>> {
val queryIndex = if (index.isNotEmpty()) table.index(index[0]) else tableByIndex!!

val pagePublisher = queryIndex.query(queryExpression)
return listOfModel(pagePublisher)
}

/**
* @implNote Bancolombia does not suggest the Scan function for DynamoDB tables due to the low performance resulting from the design of the database engine (Key value). Optimize the query using Query, GetItem or BatchGetItem functions, and if necessary, consider the Single-Table Design pattern for DynamoDB.
* @return Mono<List<E>>
* @return Mono<List></List> < E>>
* @implNote Bancolombia does not suggest the Scan function for DynamoDB tables due to the low performance resulting
* from the design of the database engine (Key value). Optimize the query using Query, GetItem or BatchGetItem
* functions, and if necessary, consider the Single-Table Design pattern for DynamoDB.
*/
@Deprecated("")
fun scan(): Mono<List<E>> {
var pagePublisher: PagePublisher<V> = customerTable.scan()
return listOfModel(pagePublisher)
return listOfModel(table.scan())
}

private fun listOfModel(pagePublisher: PagePublisher<V>): Mono<List<E>> {
return Mono.from(pagePublisher)
.map { it.items().stream().map { o -> toModel(o) }.collect(Collectors.toList()) as List<E>? }
return Mono.from(pagePublisher).map { page: Page<V> ->
page.items().stream().map { data: V -> this.toModel(data) }.toList() as List<E>?
}
}

private fun listOfModel(pagePublisher: SdkPublisher<Page<V>>): Mono<List<E>> {
return Mono.from(pagePublisher).map { page: Page<V> ->
page.items().stream().map { data: V -> this.toModel(data) }.toList() as List<E>?
}
}

private fun toEntity(model: E): V {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class TemplateAdapterOperationsTest {
new DynamoDBTemplateAdapter(dynamoDbEnhancedAsyncClient, mapper);

StepVerifier.create(dynamoDBTemplateAdapter.save(modelEntity))
.expectNextCount(0)
.expectNextCount(1)
.verifyComplete();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dependencies {
implementation(project(":model"))
implementation("org.springframework:spring-context")
implementation("com.amazonaws:aws-java-sdk-dynamodb:1.12.400")
implementation("software.amazon.awssdk:dynamodb-enhanced")
implementation("org.reactivecommons.utils:object-mapper-api:{{objectMapperVersion}}")
testImplementation("org.reactivecommons.utils:object-mapper:{{objectMapperVersion}}")
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dependencies {
implementation project(':model')
implementation 'org.springframework:spring-context'
implementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.400'
implementation 'software.amazon.awssdk:dynamodb-enhanced'
implementation 'org.reactivecommons.utils:object-mapper-api:{{objectMapperVersion}}'
testImplementation 'org.reactivecommons.utils:object-mapper:{{objectMapperVersion}}'
}
Loading
Loading