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

DynamodDB Type converters #1664

Draft
wants to merge 44 commits into
base: 5.0.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1647728
DynamodDB Type converters
sdelamo Mar 28, 2023
1892512
integer converter
sdelamo Mar 29, 2023
877d669
LocalDateTime converter
sdelamo Mar 29, 2023
aa66336
ZoneId
sdelamo Mar 29, 2023
d436119
ZoneOffset converter
sdelamo Mar 29, 2023
7d6eba4
UUID
sdelamo Mar 29, 2023
41daa1a
URL converter
sdelamo Mar 29, 2023
7845fc2
URI converter
sdelamo Mar 29, 2023
6113757
add javadoc
sdelamo Mar 29, 2023
7b65f2c
MonthDay
sdelamo Mar 29, 2023
8324597
StringBuffer and StringBuilder converters
sdelamo Mar 29, 2023
f61032e
Locale Converter
sdelamo Mar 29, 2023
e1ef110
period converter
sdelamo Mar 29, 2023
764f532
rename classes
sdelamo Mar 29, 2023
6c52553
LocalTime Converter
sdelamo Mar 29, 2023
60d09fe
ZonedDateTime converter
sdelamo Mar 29, 2023
6d4e058
AtomicBoolean converter
sdelamo Mar 29, 2023
1231e72
Long converter
sdelamo Mar 30, 2023
9cb1807
OptionalLong converter
sdelamo Mar 30, 2023
38a0445
OptionalDouble
sdelamo Mar 30, 2023
d140fae
OptionalInt
sdelamo Mar 30, 2023
70afa87
AtomicLong converter
sdelamo Mar 30, 2023
140cf1c
AtomicInteger converter
sdelamo Mar 30, 2023
4b34fc3
BigDecimal converters
sdelamo Mar 30, 2023
881f499
BigInteger converter
sdelamo Mar 30, 2023
6150c7a
Double converter
sdelamo Mar 30, 2023
219705a
Float converter
sdelamo Mar 30, 2023
95c3993
Short converter
sdelamo Mar 30, 2023
0298597
Character Converter
sdelamo Mar 30, 2023
42588d9
String converter test
sdelamo Mar 30, 2023
3d3e59c
OffsetDateTime converter
sdelamo Mar 30, 2023
3bb0902
Instant converter
sdelamo Mar 30, 2023
ff8d365
Duration Converter
sdelamo Mar 30, 2023
e54d77c
enum converter
sdelamo Mar 30, 2023
8c557ad
use prototype not singleton for converters
sdelamo Mar 30, 2023
1acd033
sessionstore example test
sdelamo Mar 30, 2023
9cd3367
ecommerce test
sdelamo Mar 31, 2023
4c1b5de
extract test into classes
sdelamo Apr 1, 2023
646d79e
update status for order
sdelamo Apr 3, 2023
c7e2afe
refactor
sdelamo Apr 3, 2023
e9fe997
more implementation
sdelamo Apr 3, 2023
30d7356
more tests
sdelamo Apr 3, 2023
a094cdb
more tests
sdelamo Apr 4, 2023
3d2ce9c
more tests
sdelamo Apr 4, 2023
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
33 changes: 33 additions & 0 deletions dynamodb/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
plugins {
id("io.micronaut.build.internal.aws-module")
}
dependencies {
annotationProcessor(mnValidation.micronaut.validation.processor)
api(platform(libs.boms.aws.java.sdk.v2))
api(libs.awssdk.dynamodb)
implementation(mnValidation.micronaut.validation)

testAnnotationProcessor(platform(mn.micronaut.core.bom))
testAnnotationProcessor(mn.micronaut.inject.java)

testImplementation(platform(mn.micronaut.core.bom))
testImplementation(libs.junit.jupiter.api)
testImplementation(mnTest.micronaut.test.junit5)
testRuntimeOnly(libs.junit.jupiter.engine)

testImplementation(projects.micronautAwsSdkV2)

testAnnotationProcessor(mnSerde.micronaut.serde.processor)
testImplementation(mnSerde.micronaut.serde.jackson)
testImplementation(mn.micronaut.http.server.netty)
testImplementation(mn.micronaut.http.client)
testImplementation(platform(libs.testcontainers.bom))
testImplementation(libs.testcontainers)

testImplementation(libs.ksuid)
}
micronautBuild {
binaryCompatibility {
enabled.set(false)
}
}
55 changes: 55 additions & 0 deletions dynamodb/src/main/java/io/micronaut/aws/dynamodb/CompositeKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2017-2023 original authors
*
* 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
*
* https://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 io.micronaut.aws.dynamodb;

import io.micronaut.core.annotation.NonNull;

/**
* Composite Key for an Amazon DynamodDB Single table design.
* @author Sergio del Amo
* @since 4.0.0
*/
public interface CompositeKey {

/**
*
* @return Partition or Hash Key
*/
@NonNull
String getPartionKey();

/**
*
* @return Sort Key
*/
@NonNull
String getSortKey();

@NonNull
static CompositeKey of(@NonNull String hashKey, @NonNull String sortKey) {
return new CompositeKey() {
@Override
public String getPartionKey() {
return hashKey;
}

@Override
public String getSortKey() {
return sortKey;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright 2017-2023 original authors
*
* 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
*
* https://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 io.micronaut.aws.dynamodb;

import io.micronaut.core.beans.BeanIntrospection;
import io.micronaut.core.beans.BeanIntrospector;
import io.micronaut.core.beans.BeanProperty;
import io.micronaut.core.beans.BeanWrapper;
import io.micronaut.core.beans.exceptions.IntrospectionException;
import io.micronaut.core.convert.ConversionService;
import jakarta.inject.Singleton;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
* {@link io.micronaut.context.annotation.DefaultImplementation} of {@link DynamoDbConversionService} which uses {@link ConversionService} to convert from and to {@link AttributeValue} map.
*
*/
@Singleton
public class DefaultDynamoDbConversionService implements DynamoDbConversionService {

private final ConversionService conversionService;

public DefaultDynamoDbConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}

@Override
public <S> Map<String, AttributeValue> convert(BeanWrapper<S> wrapper) {
Map<String, AttributeValue> result = new HashMap<>();
S bean = wrapper.getBean();
for (BeanProperty<S, ?> beanProperty : wrapper.getBeanProperties()) {

if (isStringSet(bean, beanProperty)) {
Set<String> stringSet = new HashSet<>();
for (Object item : (Set) beanProperty.get(bean)) {
stringSet.add(item.toString());
}
result.put(beanProperty.getName(), AttributeValue.builder().ss(stringSet).build());
} else {
Optional<AttributeValue> attributeValueOptional = conversionService.convert(beanProperty.get(bean), AttributeValue.class);
if (attributeValueOptional.isPresent()) {
AttributeValue attributeValue = attributeValueOptional.get();
result.put(beanProperty.getName(), attributeValue);
} else {


try {
BeanWrapper valueWrapper = BeanWrapper.getWrapper(beanProperty.get(bean));
Map<String, AttributeValue> valueWrapperMap = convert(valueWrapper);
AttributeValue attributeValue = AttributeValue.builder().m(valueWrapperMap).build();
result.put(beanProperty.getName(), attributeValue);
} catch (IntrospectionException e) {

}
}
}
}
return result;
}

@Override
public <T> T convert(Map<String, AttributeValue> item, Class<T> targetType) {
final BeanIntrospection<T> introspection = BeanIntrospection.getIntrospection(targetType);
Object[] arguments = new Object[introspection.getConstructorArguments().length];
int counter = 0;
for (BeanProperty beanProperty : introspection.getBeanProperties()) {
if (item.containsKey(beanProperty.getName())) {

AttributeValue attributeValue = item.get(beanProperty.getName());
if (attributeValue.hasSs()) {
arguments[counter++] = conversionService.convert(attributeValue.ss(), beanProperty.getType()).orElse(null);
} else {
if (BeanIntrospector.SHARED.findIntrospection(beanProperty.getType()).isPresent()) {
Map<String, AttributeValue> m = attributeValue.m();
if (m != null) {
arguments[counter++] = convert(m, beanProperty.getType());
}
} else {
arguments[counter++] = conversionService.convert(attributeValue, beanProperty.getType())
.orElse(null);
}
}
} else {
arguments[counter++] = null;
}
}
return introspection.instantiate(arguments);
}

private <S> boolean isStringSet(S bean, BeanProperty<S, ?> beanProperty) {
if (!Set.class.isAssignableFrom(beanProperty.getType())) {
return false;
}
for (Object setItem : (Set) beanProperty.get(bean)) {
if (!CharSequence.class.isAssignableFrom(setItem.getClass())) {
return false;
}
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2017-2023 original authors
*
* 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
*
* https://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 io.micronaut.aws.dynamodb;

import io.micronaut.context.annotation.DefaultImplementation;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.beans.BeanWrapper;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import io.micronaut.core.beans.exceptions.IntrospectionException;
import java.util.Map;

/**
* @author Sergio del Amo
* @since 4.0.0
*/
@DefaultImplementation(DefaultDynamoDbConversionService.class)
public interface DynamoDbConversionService {
@NonNull
<S> Map<String, AttributeValue> convert(@NonNull BeanWrapper<S> wrapper);

@NonNull
default Map<String, AttributeValue> convert(@NonNull Object object) throws IntrospectionException {
return convert(BeanWrapper.getWrapper(object));
}

@NonNull
<T> T convert(@NonNull Map<String, AttributeValue> item, Class<T> targetType);
}
Loading