-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Maximillian Arruda <dearrudam@gmail.com>
- Loading branch information
Showing
4 changed files
with
263 additions
and
151 deletions.
There are no files selected for viewing
145 changes: 145 additions & 0 deletions
145
...ain/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/* | ||
* Copyright (c) 2023 Contributors to the Eclipse Foundation | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v1.0 | ||
* and Apache License v2.0 which accompanies this distribution. | ||
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | ||
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. | ||
* | ||
* You may elect to redistribute this code under either of these licenses. | ||
* | ||
* Contributors: | ||
* | ||
* Maximillian Arruda | ||
*/ | ||
|
||
package org.eclipse.jnosql.databases.dynamodb.communication; | ||
|
||
import jakarta.json.bind.Jsonb; | ||
import org.eclipse.jnosql.communication.document.Document; | ||
import org.eclipse.jnosql.communication.document.DocumentEntity; | ||
import org.eclipse.jnosql.communication.driver.JsonbSupplier; | ||
import org.eclipse.jnosql.communication.driver.ValueUtil; | ||
import software.amazon.awssdk.core.SdkBytes; | ||
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; | ||
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; | ||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue; | ||
|
||
import java.util.*; | ||
import java.util.function.Consumer; | ||
import java.util.function.UnaryOperator; | ||
import java.util.stream.StreamSupport; | ||
|
||
import static java.util.Collections.singletonMap; | ||
import static java.util.stream.Collectors.toList; | ||
|
||
class DocumentEntityConverter { | ||
|
||
static final String ENTITY = "@entity"; | ||
static final String ID = "_id"; | ||
private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); | ||
|
||
private DocumentEntityConverter() { | ||
} | ||
|
||
static DocumentEntity toDocumentEntity(UnaryOperator<String> entityNameResolver, EnhancedDocument enhancedDocument) { | ||
if (enhancedDocument == null) { | ||
return null; | ||
} | ||
if (enhancedDocument.toMap().isEmpty()) { | ||
return null; | ||
} | ||
UnaryOperator<String> resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); | ||
String entityAttribute = resolver.apply(ENTITY); | ||
Map<String, AttributeValue> map = enhancedDocument.toMap(); | ||
var entityName = map.containsKey(entityAttribute) ? map.get(entityAttribute).s() : entityAttribute; | ||
List<Document> documents = map.entrySet() | ||
.stream() | ||
.filter(entry -> !Objects.equals(entityAttribute, entry.getKey())) | ||
.map(entry -> Document.of(entry.getKey(), convertValue(entry.getValue()))) | ||
.toList(); | ||
return DocumentEntity.of(entityName, documents); | ||
} | ||
|
||
private static Object convertValue(Object value) { | ||
if (value instanceof AttributeValue attributeValue) { | ||
switch (attributeValue.type()) { | ||
case S: | ||
return attributeValue.s(); | ||
case N: | ||
return Double.valueOf(attributeValue.n()); | ||
case B: | ||
return attributeValue.b().asByteArray(); | ||
case SS: | ||
return attributeValue.ss(); | ||
case NS: | ||
return attributeValue.ns().stream().map(Double::valueOf).toList(); | ||
case BS: | ||
return attributeValue.bs().stream().map(SdkBytes::asByteArray).toList(); | ||
case L: | ||
return attributeValue.l().stream().map(DocumentEntityConverter::convertValue).toList(); | ||
case M: | ||
return attributeValue.m().entrySet().stream().map(e -> Document.of(e.getKey(), convertValue(e.getValue()))).toList(); | ||
case NUL: | ||
return null; | ||
case BOOL: | ||
return attributeValue.bool(); | ||
case UNKNOWN_TO_SDK_VERSION: | ||
default: | ||
return null; // map type | ||
} | ||
} | ||
return value; | ||
} | ||
|
||
static EnhancedDocument toEnhancedDocument(UnaryOperator<String> entityNameResolver, DocumentEntity documentEntity) { | ||
UnaryOperator<String> resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); | ||
Map<String, Object> documentAsMap = getMap(resolver, documentEntity); | ||
return EnhancedDocument.builder() | ||
.json(JSONB.toJson(documentAsMap)) | ||
.build(); | ||
} | ||
|
||
static Map<String, Object> getMap(UnaryOperator<String> entityNameResolver, DocumentEntity entity) { | ||
var nameResolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); | ||
Map<String, Object> jsonObject = new HashMap<>(); | ||
entity.documents().forEach(feedJSON(jsonObject)); | ||
jsonObject.put(Optional.ofNullable(nameResolver.apply(ENTITY)).orElse(ENTITY), entity.name()); | ||
return jsonObject; | ||
} | ||
|
||
private static Consumer<Document> feedJSON(Map<String, Object> jsonObject) { | ||
return d -> { | ||
Object value = ValueUtil.convert(d.value()); | ||
if (value instanceof Document) { | ||
Document subDocument = Document.class.cast(value); | ||
jsonObject.put(d.name(), singletonMap(subDocument.name(), subDocument.get())); | ||
} else if (isSudDocument(value)) { | ||
Map<String, Object> subDocument = getMap(value); | ||
jsonObject.put(d.name(), subDocument); | ||
} else if (isSudDocumentList(value)) { | ||
jsonObject.put(d.name(), StreamSupport.stream(Iterable.class.cast(value).spliterator(), false) | ||
.map(DocumentEntityConverter::getMap).collect(toList())); | ||
} else { | ||
jsonObject.put(d.name(), value); | ||
} | ||
}; | ||
} | ||
|
||
private static Map<String, Object> getMap(Object value) { | ||
Map<String, Object> subDocument = new HashMap<>(); | ||
StreamSupport.stream(Iterable.class.cast(value).spliterator(), | ||
false).forEach(feedJSON(subDocument)); | ||
return subDocument; | ||
} | ||
|
||
private static boolean isSudDocument(Object value) { | ||
return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). | ||
allMatch(org.eclipse.jnosql.communication.document.Document.class::isInstance); | ||
} | ||
|
||
private static boolean isSudDocumentList(Object value) { | ||
return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). | ||
allMatch(d -> d instanceof Iterable && isSudDocument(d)); | ||
} | ||
} |
88 changes: 0 additions & 88 deletions
88
.../org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java
This file was deleted.
Oops, something went wrong.
118 changes: 118 additions & 0 deletions
118
...java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* | ||
* Copyright (c) 2023 Contributors to the Eclipse Foundation | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v1.0 | ||
* and Apache License v2.0 which accompanies this distribution. | ||
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | ||
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. | ||
* | ||
* You may elect to redistribute this code under either of these licenses. | ||
* | ||
* Contributors: | ||
* | ||
* Maximillian Arruda | ||
*/ | ||
|
||
package org.eclipse.jnosql.databases.dynamodb.communication; | ||
|
||
import jakarta.json.Json; | ||
import jakarta.json.bind.Jsonb; | ||
import org.eclipse.jnosql.communication.TypeReference; | ||
import org.eclipse.jnosql.communication.document.Document; | ||
import org.eclipse.jnosql.communication.driver.JsonbSupplier; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.condition.EnabledIfSystemProperty; | ||
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; | ||
|
||
import java.io.StringReader; | ||
import java.util.List; | ||
import java.util.function.UnaryOperator; | ||
|
||
import static org.assertj.core.api.SoftAssertions.assertSoftly; | ||
import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; | ||
import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; | ||
|
||
@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) | ||
class DocumentEntityConverterTest { | ||
|
||
private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); | ||
private static final UnaryOperator<String> entityNameResolver = UnaryOperator.identity(); | ||
|
||
|
||
@Test | ||
void shouldConvertDocumentEntityToEnhancedDocument() { | ||
|
||
assertSoftly(softly -> { | ||
var entity = DocumentEntityGenerator.getEntity(); | ||
var enhancedDocument = DocumentEntityConverter.toEnhancedDocument(entityNameResolver, entity); | ||
var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); | ||
var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); | ||
softly.assertThat(actual).as("cannot convert a simple DocumentEntity") | ||
.isEqualTo(expected); | ||
}); | ||
} | ||
|
||
@Test | ||
void shouldConvertDocumentEntityWithSubDocumentsToEnhancedDocument() { | ||
|
||
assertSoftly(softly -> { | ||
var entity = DocumentEntityGenerator.getEntityWithSubDocuments(3); | ||
var enhancedDocument = DocumentEntityConverter.toEnhancedDocument(entityNameResolver, entity); | ||
var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); | ||
var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); | ||
softly.assertThat(actual).as("cannot convert a DocumentEntity with document sublist") | ||
.isEqualTo(expected); | ||
}); | ||
} | ||
|
||
|
||
@Test | ||
void shouldConvertEnhancedDocumentToDocumentEntity() { | ||
|
||
var enhancedDocument = EnhancedDocument.builder() | ||
.json(""" | ||
{ | ||
"%s":"Max", | ||
"%s": "person", | ||
"name":"Maximillian", | ||
"number": 123, | ||
"address": { | ||
"street": "Rua tralala" | ||
}, | ||
"phones": [ | ||
"1111-2222", | ||
"2222-3333" | ||
] | ||
} | ||
""".formatted(DocumentEntityConverter.ID, DocumentEntityConverter.ENTITY)).build(); | ||
var expected = DocumentEntityConverter.toDocumentEntity(entityNameResolver, enhancedDocument); | ||
|
||
assertSoftly(softly -> { | ||
softly.assertThat(expected).as("cannot return a null reference") | ||
.isNotNull(); | ||
softly.assertThat(expected.name()).as("documentEntity name is not correct") | ||
.isEqualTo("person"); | ||
|
||
softly.assertThat(expected.find("_id", String.class)) | ||
.as("documentEntity._id was parsed incorrectly") | ||
.hasValue("Max"); | ||
softly.assertThat(expected.find("name", String.class)) | ||
.as("documentEntity.name was parsed incorrectly") | ||
.hasValue("Maximillian"); | ||
softly.assertThat(expected.find("number", Integer.class)) | ||
.as("documentEntity.number was parsed incorrectly") | ||
.hasValue(123); | ||
softly.assertThat(expected.find("address", new TypeReference<List<Document>>() { | ||
})) | ||
.as("documentEntity.address was parsed incorrectly") | ||
.contains(List.of(Document.of("street", "Rua tralala"))); | ||
softly.assertThat(expected.find("phones", new TypeReference<List<String>>() { | ||
})) | ||
.as("documentEntity.phones was parsed incorrectly") | ||
.contains(List.of("1111-2222", "2222-3333")); | ||
|
||
}); | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.