diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 9edf6283e..5df08fdfc 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -16,6 +16,7 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version - Include condition to is NUll and is Not Null in the query - Include pagination with Query annotation - Add support to array in the fields +- Add support to array in the fields of java record classes === Fixed diff --git a/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/ClassGraphClassScannerTest.java b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/ClassGraphClassScannerTest.java index 88fcd6257..dee374f16 100644 --- a/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/ClassGraphClassScannerTest.java +++ b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/ClassGraphClassScannerTest.java @@ -15,7 +15,6 @@ package org.eclipse.jnosql.mapping.reflection; import jakarta.data.repository.CrudRepository; -import jakarta.data.repository.BasicRepository; import org.eclipse.jnosql.mapping.NoSQLRepository; import org.eclipse.jnosql.mapping.reflection.entities.AnimalRepository; import org.eclipse.jnosql.mapping.reflection.entities.Contact; @@ -40,7 +39,7 @@ class ClassGraphClassScannerTest { void shouldReturnEntities() { Set> entities = classScanner.entities(); Assertions.assertNotNull(entities); - assertThat(entities).hasSize(29) + assertThat(entities).hasSize(31) .contains(Person.class); } diff --git a/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataTest.java b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataTest.java index 6c597ff32..42618443c 100644 --- a/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataTest.java +++ b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataTest.java @@ -11,56 +11,30 @@ * Contributors: * * Otavio Santana + * Maximillian Arruda */ package org.eclipse.jnosql.mapping.reflection; import org.eclipse.jnosql.mapping.metadata.ArrayParameterMetaData; -import org.eclipse.jnosql.mapping.metadata.ClassConverter; -import org.eclipse.jnosql.mapping.metadata.CollectionParameterMetaData; -import org.eclipse.jnosql.mapping.metadata.ConstructorMetadata; -import org.eclipse.jnosql.mapping.metadata.EntityMetadata; -import org.eclipse.jnosql.mapping.reflection.entities.Book; -import org.eclipse.jnosql.mapping.reflection.entities.constructor.BookUser; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; -class DefaultArrayParameterMetaDataTest { - - private ArrayParameterMetaData fieldMetadata; +interface DefaultArrayParameterMetaDataTest { - @BeforeEach - void setUp(){ - ClassConverter converter = new ReflectionClassConverter(); - EntityMetadata entityMetadata = converter.apply(BookUser.class); - ConstructorMetadata constructor = entityMetadata.constructor(); - this.fieldMetadata = (ArrayParameterMetaData) - constructor.parameters().stream().filter(p -> p.name().equals("magazines")) - .findFirst().orElseThrow(); - } + ArrayParameterMetaData fieldMetadata(); @Test - void shouldToString() { - assertThat(fieldMetadata.toString()).isNotEmpty().isNotNull(); + default void shouldToString() { + assertThat(fieldMetadata().toString()).isNotEmpty().isNotNull(); } - @Test - void shouldGetElementType(){ - assertThat(fieldMetadata.elementType()).isEqualTo(Book.class); - } + Class expectedElementType(); @Test - void shouldArrayInstance() { - List magazines = List.of(Book.builder().build(), Book.builder().build()); - - Book[] value = (Book[]) fieldMetadata.arrayInstance(magazines); - assertThat(value).containsExactlyElementsOf(magazines); + default void shouldGetElementType() { + assertThat(fieldMetadata().elementType()).isEqualTo(expectedElementType()); } } \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataWhenEntityClassIsRecordTest.java b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataWhenEntityClassIsRecordTest.java new file mode 100644 index 000000000..d013ac5b6 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataWhenEntityClassIsRecordTest.java @@ -0,0 +1,40 @@ +/* + * 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.mapping.reflection; + +import org.eclipse.jnosql.mapping.metadata.ArrayParameterMetaData; +import org.eclipse.jnosql.mapping.metadata.ClassConverter; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.reflection.entities.constructor.Game; +import org.eclipse.jnosql.mapping.reflection.entities.constructor.Player; + +class DefaultArrayParameterMetaDataWhenEntityClassIsRecordTest implements DefaultArrayParameterMetaDataTest { + + @Override + public ArrayParameterMetaData fieldMetadata() { + ClassConverter converter = new ReflectionClassConverter(); + EntityMetadata entityMetadata = converter.apply(Player.class); + var constructor = entityMetadata.constructor(); + return (ArrayParameterMetaData) + constructor.parameters().stream().filter(p -> p.name().equals("games")) + .findFirst().orElseThrow(); + } + + @Override + public Class expectedElementType() { + return Game.class; + } + +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataWhenEntityIsRegularClassTest.java b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataWhenEntityIsRegularClassTest.java new file mode 100644 index 000000000..f802d135d --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/DefaultArrayParameterMetaDataWhenEntityIsRegularClassTest.java @@ -0,0 +1,54 @@ +/* + * 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: + * + * Otavio Santana + * Maximillian Arruda + */ +package org.eclipse.jnosql.mapping.reflection; + +import org.eclipse.jnosql.mapping.metadata.ArrayParameterMetaData; +import org.eclipse.jnosql.mapping.metadata.ClassConverter; +import org.eclipse.jnosql.mapping.metadata.ConstructorMetadata; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.reflection.entities.Book; +import org.eclipse.jnosql.mapping.reflection.entities.constructor.BookUser; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class DefaultArrayParameterMetaDataWhenEntityIsRegularClassTest implements DefaultArrayParameterMetaDataTest { + + @Override + public ArrayParameterMetaData fieldMetadata() { + ClassConverter converter = new ReflectionClassConverter(); + EntityMetadata entityMetadata = converter.apply(BookUser.class); + ConstructorMetadata constructor = entityMetadata.constructor(); + return (ArrayParameterMetaData) + constructor.parameters().stream().filter(p -> p.name().equals("magazines")) + .findFirst().orElseThrow(); + } + + @Override + public Class expectedElementType() { + return Book.class; + } + + + @Test + void shouldArrayInstance() { + List magazines = List.of(Book.builder().build(), Book.builder().build()); + Book[] value = (Book[]) fieldMetadata().arrayInstance(magazines); + assertThat(value).containsExactlyElementsOf(magazines); + } + } \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/entities/constructor/Game.java b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/entities/constructor/Game.java new file mode 100644 index 000000000..313e17534 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/entities/constructor/Game.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 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: + * + * Otavio Santana + * Maximillian Arruda + */ +package org.eclipse.jnosql.mapping.reflection.entities.constructor; + +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +@Entity +public record Game(@Id String name) { +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/entities/constructor/Player.java b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/entities/constructor/Player.java new file mode 100644 index 000000000..7d530551a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-reflection/src/test/java/org/eclipse/jnosql/mapping/reflection/entities/constructor/Player.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 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: + * + * Otavio Santana + * Maximillian Arruda + */ +package org.eclipse.jnosql.mapping.reflection.entities.constructor; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +@Entity +public record Player(@Id String nickname, @Column Game[] games) { +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/semistructured/ParameterConverter.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/semistructured/ParameterConverter.java index d850f98db..c88d9b2ca 100644 --- a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/semistructured/ParameterConverter.java +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/semistructured/ParameterConverter.java @@ -11,17 +11,20 @@ * Contributors: * * Otavio Santana + * Maximillian Arruda */ package org.eclipse.jnosql.mapping.semistructured; import org.eclipse.jnosql.communication.TypeReference; import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.mapping.metadata.ArrayParameterMetaData; +import org.eclipse.jnosql.mapping.metadata.CollectionParameterMetaData; import org.eclipse.jnosql.mapping.metadata.ConstructorBuilder; import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; -import org.eclipse.jnosql.mapping.metadata.CollectionParameterMetaData; import org.eclipse.jnosql.mapping.metadata.MapParameterMetaData; import org.eclipse.jnosql.mapping.metadata.ParameterMetaData; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -58,7 +61,8 @@ void convert(EntityConverter converter, Element element, ParameterMetaData metaD builder.add(entity); } else { - List columns = element.get(new TypeReference<>() {}); + List columns = element.get(new TypeReference<>() { + }); Object entity = converter.toEntity(metaData.type(), columns); builder.add(entity); } @@ -86,6 +90,21 @@ void convert(EntityConverter converter, Element element, ParameterMetaData metaD Object value = mapParameterMetaData.value(element.value()); builder.add(value); } + + }, ARRAY { + @SuppressWarnings("unchecked") + @Override + void convert(EntityConverter converter, Element element, ParameterMetaData metaData, ConstructorBuilder builder) { + var arrayParameterMetaData = (ArrayParameterMetaData) metaData; + List> embeddable = (List>) element.get(); + var elements = Array.newInstance(arrayParameterMetaData.elementType(), embeddable.size()); + int index = 0; + for (List elementsList : embeddable) { + Object item = converter.toEntity(arrayParameterMetaData.elementType(), elementsList); + Array.set(elements, index++, item); + } + builder.add(elements); + } }; abstract void convert(EntityConverter converter, @@ -94,19 +113,30 @@ abstract void convert(EntityConverter converter, static ParameterConverter of(ParameterMetaData parameter, EntitiesMetadata entities) { return switch (parameter.mappingType()) { - case COLLECTION -> validateCollection(parameter, entities); + case COLLECTION -> collectionConverter(parameter, entities); + case ARRAY -> arrayConverter(parameter, entities); case MAP -> MAP; case ENTITY, EMBEDDED_GROUP -> ENTITY; default -> DEFAULT; }; } - private static ParameterConverter validateCollection(ParameterMetaData parameter, EntitiesMetadata entities) { - CollectionParameterMetaData genericParameter = (CollectionParameterMetaData) parameter; + private static ParameterConverter collectionConverter(ParameterMetaData parameter, EntitiesMetadata entities) { + var genericParameter = (CollectionParameterMetaData) parameter; Class type = genericParameter.elementType(); if (entities.findByClassName(type.getName()).isPresent()) { return COLLECTION; } return DEFAULT; } + + private static ParameterConverter arrayConverter(ParameterMetaData parameter, EntitiesMetadata entities) { + var genericParameter = (ArrayParameterMetaData) parameter; + Class type = genericParameter.elementType(); + if (entities.findByClassName(type.getName()).isPresent()) { + return ARRAY; + } + return DEFAULT; + } + } diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/EntityConverterConstructorTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/EntityConverterConstructorTest.java index 5bf65be75..965e12c6c 100644 --- a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/EntityConverterConstructorTest.java +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/EntityConverterConstructorTest.java @@ -20,24 +20,21 @@ import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.semistructured.entities.Animal; import org.eclipse.jnosql.mapping.semistructured.entities.Book; import org.eclipse.jnosql.mapping.semistructured.entities.BookRelease; -import org.eclipse.jnosql.mapping.semistructured.entities.Form; import org.eclipse.jnosql.mapping.semistructured.entities.Money; -import org.eclipse.jnosql.mapping.semistructured.entities.SocialMediaContact; -import org.eclipse.jnosql.mapping.semistructured.entities.Wine; -import org.eclipse.jnosql.mapping.semistructured.entities.WineFactory; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.Beer; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.BeerFactory; +import org.eclipse.jnosql.mapping.semistructured.entities.constructor.BookBag; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.BookUser; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.Computer; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.PetOwner; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.SocialMediaFollowers; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.SocialMediaFollowersRecord; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.SuperHero; -import org.eclipse.jnosql.mapping.reflection.Reflections; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; import org.eclipse.jnosql.mapping.semistructured.entities.constructor.Survey; import org.jboss.weld.junit5.auto.AddExtensions; import org.jboss.weld.junit5.auto.AddPackages; @@ -120,7 +117,8 @@ void shouldConvertPetOwnerCommunication() { assertNotNull(communication); assertEquals(10L, communication.find("_id", Long.class).get()); assertEquals("Poliana", communication.find("name", String.class).get()); - List columns = communication.find("animal", new TypeReference>() {}) + List columns = communication.find("animal", new TypeReference>() { + }) .get(); assertThat(columns).contains(Element.of("name", "Ada")); } @@ -208,35 +206,35 @@ void shouldConvertHero() { } @Test - void shouldIgnoreWhenNullAtConstructor(){ + void shouldIgnoreWhenNullAtConstructor() { CommunicationEntity entity = CommunicationEntity.of("SocialMediaFollowers"); entity.add("_id", "id"); entity.add("followers", null); SocialMediaFollowers socialMediaContact = converter.toEntity(entity); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { soft.assertThat(socialMediaContact).isNotNull(); soft.assertThat(socialMediaContact.getId()).isEqualTo("id"); }); } @Test - void shouldIgnoreWhenNullAtRecord(){ + void shouldIgnoreWhenNullAtRecord() { CommunicationEntity entity = CommunicationEntity.of("SocialMediaFollowersRecord"); entity.add("_id", "id"); entity.add("followers", null); SocialMediaFollowersRecord socialMediaContact = converter.toEntity(entity); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { soft.assertThat(socialMediaContact).isNotNull(); soft.assertThat(socialMediaContact.id()).isEqualTo("id"); }); } @Test - void shouldConvertGroupEmbeddable(){ + void shouldConvertGroupEmbeddable() { CommunicationEntity entity = CommunicationEntity.of("Beer"); entity.add("_id", "id"); entity.add("name", "Vin Blanc"); @@ -245,7 +243,7 @@ void shouldConvertGroupEmbeddable(){ Beer beer = converter.toEntity(entity); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { var factory = beer.factory(); soft.assertThat(beer).isNotNull(); soft.assertThat(beer.id()).isEqualTo("id"); @@ -257,20 +255,21 @@ void shouldConvertGroupEmbeddable(){ } @Test - void shouldConvertGroupEmbeddableToCommunication(){ + void shouldConvertGroupEmbeddableToCommunication() { var wine = Beer.of("id", "Vin Blanc", BeerFactory.of("Napa Valley Factory", "Napa Valley")); var communication = converter.toCommunication(wine); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { soft.assertThat(communication).isNotNull(); soft.assertThat(communication.name()).isEqualTo("Beer"); soft.assertThat(communication.find("_id").orElseThrow().get()).isEqualTo("id"); soft.assertThat(communication.find("name").orElseThrow().get()).isEqualTo("Vin Blanc"); communication.find("factory").ifPresent(e -> { - List elements = e.get(new TypeReference<>(){}); + List elements = e.get(new TypeReference<>() { + }); soft.assertThat(elements).hasSize(2); soft.assertThat(elements.stream().filter(c -> "name".equals(c.name())).findFirst().orElseThrow().get()) .isEqualTo("Napa Valley Factory"); @@ -289,7 +288,7 @@ void shouldConvertGenericTypes() { Element.of("question1", true), Element.of("question2", false), Element.of("question3", List.of(Element.of("advanced", true), - Element.of("visible", "true"))) + Element.of("visible", "true"))) )); Survey survey = converter.toEntity(communication); @@ -303,4 +302,63 @@ void shouldConvertGenericTypes() { } + @Test + void shouldConvertArrayTypes() { + + var effectiveJava = Book.builder() + .withId(10L) + .withName("Effective Java") + .withAge(Year.now().minusYears(2018).getValue()) + .build(); + + var cleanCode = Book.builder() + .withId(12L) + .withName("Clean Code") + .withAge(Year.now().minusYears(2008).getValue()) + .build(); + + CommunicationEntity communication = CommunicationEntity.of(BookBag.class.getSimpleName()); + communication.add("_id", "Max"); + List> columns = new ArrayList<>(); + columns.add(Arrays.asList( + Element.of("_id", effectiveJava.getId()), + Element.of("name", effectiveJava.getName()), + Element.of("age", effectiveJava.getAge()))); + columns.add(Arrays.asList( + Element.of("_id", cleanCode.getId()), + Element.of("name", cleanCode.getName()), + Element.of("age", cleanCode.getAge()))); + + communication.add("books", columns); + + BookBag bookBag = converter.toEntity(communication); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(bookBag.owner()) + .as("invalid first level String field conversion") + .isEqualTo("Max"); + + List names = Arrays.asList(bookBag.books()); + + Book[] books = bookBag.books(); + + softly.assertThat(books) + .as("invalid first level Array field conversion: check size") + .isNotNull() + .hasSize(2); + + softly.assertThat(books[0]) + .as("invalid first level Array field conversion: check first element") + .usingRecursiveComparison() + .isEqualTo(effectiveJava); + + softly.assertThat(books[1]) + .as("invalid first level Array field conversion: check second element") + .usingRecursiveComparison() + .isEqualTo(cleanCode); + + }); + + } + } diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/EntityConverterTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/EntityConverterTest.java index 2e34baae7..5ccb25523 100644 --- a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/EntityConverterTest.java +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/EntityConverterTest.java @@ -21,9 +21,12 @@ import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.semistructured.entities.Actor; import org.eclipse.jnosql.mapping.semistructured.entities.Address; import org.eclipse.jnosql.mapping.semistructured.entities.AppointmentBook; +import org.eclipse.jnosql.mapping.semistructured.entities.Book; import org.eclipse.jnosql.mapping.semistructured.entities.Citizen; import org.eclipse.jnosql.mapping.semistructured.entities.Contact; import org.eclipse.jnosql.mapping.semistructured.entities.ContactType; @@ -46,8 +49,7 @@ import org.eclipse.jnosql.mapping.semistructured.entities.Worker; import org.eclipse.jnosql.mapping.semistructured.entities.WorkflowStep; import org.eclipse.jnosql.mapping.semistructured.entities.ZipCode; -import org.eclipse.jnosql.mapping.reflection.Reflections; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.semistructured.entities.constructor.BookBag; import org.jboss.weld.junit5.auto.AddExtensions; import org.jboss.weld.junit5.auto.AddPackages; import org.jboss.weld.junit5.auto.EnableAutoWeld; @@ -56,6 +58,7 @@ import org.junit.jupiter.api.Test; import java.math.BigDecimal; +import java.time.Year; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -65,13 +68,18 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.function.BiConsumer; import java.util.stream.Stream; import static java.util.Arrays.asList; import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.jnosql.mapping.semistructured.entities.StepTransitionReason.REPEAT; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; @EnableAutoWeld @AddPackages(value = {Converters.class, EntityConverter.class}) @@ -485,9 +493,9 @@ void shouldCreateUserScope() { UserScope user = converter.toEntity(entity); Assertions.assertNotNull(user); - Assertions.assertEquals("userName",user.getUserName()); - Assertions.assertEquals("scope",user.getScope()); - Assertions.assertEquals(Collections.singletonMap("halo", "weld"),user.getProperties()); + Assertions.assertEquals("userName", user.getUserName()); + Assertions.assertEquals("scope", user.getScope()); + Assertions.assertEquals(Collections.singletonMap("halo", "weld"), user.getProperties()); } @@ -500,9 +508,9 @@ void shouldCreateUserScope2() { UserScope user = converter.toEntity(entity); Assertions.assertNotNull(user); - Assertions.assertEquals("userName",user.getUserName()); - Assertions.assertEquals("scope",user.getScope()); - Assertions.assertEquals(Collections.singletonMap("halo", "weld"),user.getProperties()); + Assertions.assertEquals("userName", user.getUserName()); + Assertions.assertEquals("scope", user.getScope()); + Assertions.assertEquals(Collections.singletonMap("halo", "weld"), user.getProperties()); } @@ -535,7 +543,7 @@ void shouldReturnNullValuePresent() { } @Test - void shouldConvertWorkflow(){ + void shouldConvertWorkflow() { var workflowStep = WorkflowStep.builder() .id("id") .key("key") @@ -551,7 +559,7 @@ void shouldConvertWorkflow(){ var document = this.converter.toCommunication(workflowStep); WorkflowStep result = this.converter.toEntity(document); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { soft.assertThat(result).isNotNull(); soft.assertThat(result.id()).isEqualTo("id"); soft.assertThat(result.key()).isEqualTo("key"); @@ -586,7 +594,7 @@ void shouldUpdateEmbeddable2() { .build(); var document = this.converter.toCommunication(workflowStep); WorkflowStep result = this.converter.toEntity(document); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { soft.assertThat(result).isNotNull(); soft.assertThat(result.id()).isEqualTo("id"); soft.assertThat(result.key()).isEqualTo("key"); @@ -603,7 +611,7 @@ void shouldUpdateEmbeddable2() { } @Test - void shouldIgnoreWhenNull(){ + void shouldIgnoreWhenNull() { CommunicationEntity entity = CommunicationEntity.of("SocialMediaContact"); entity.add("_id", "id"); entity.add("name", "Twitter"); @@ -611,7 +619,7 @@ void shouldIgnoreWhenNull(){ SocialMediaContact socialMediaContact = converter.toEntity(entity); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { soft.assertThat(socialMediaContact).isNotNull(); soft.assertThat(socialMediaContact.getId()).isEqualTo("id"); soft.assertThat(socialMediaContact.getName()).isEqualTo("Twitter"); @@ -620,7 +628,7 @@ void shouldIgnoreWhenNull(){ } @Test - void shouldConvertGroupEmbeddable(){ + void shouldConvertGroupEmbeddable() { CommunicationEntity entity = CommunicationEntity.of("Wine"); entity.add("_id", "id"); entity.add("name", "Vin Blanc"); @@ -629,7 +637,7 @@ void shouldConvertGroupEmbeddable(){ Wine wine = converter.toEntity(entity); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { WineFactory factory = wine.getFactory(); soft.assertThat(wine).isNotNull(); soft.assertThat(wine.getId()).isEqualTo("id"); @@ -641,20 +649,21 @@ void shouldConvertGroupEmbeddable(){ } @Test - void shouldConvertGroupEmbeddableToCommunication(){ + void shouldConvertGroupEmbeddableToCommunication() { Wine wine = Wine.of("id", "Vin Blanc", WineFactory.of("Napa Valley Factory", "Napa Valley")); var communication = converter.toCommunication(wine); - SoftAssertions.assertSoftly(soft ->{ + SoftAssertions.assertSoftly(soft -> { soft.assertThat(communication).isNotNull(); soft.assertThat(communication.name()).isEqualTo("Wine"); soft.assertThat(communication.find("_id").orElseThrow().get()).isEqualTo("id"); soft.assertThat(communication.find("name").orElseThrow().get()).isEqualTo("Vin Blanc"); communication.find("factory").ifPresent(e -> { - List elements = e.get(new TypeReference<>(){}); + List elements = e.get(new TypeReference<>() { + }); soft.assertThat(elements).hasSize(2); soft.assertThat(elements.stream().filter(c -> "name".equals(c.name())).findFirst().orElseThrow().get()) .isEqualTo("Napa Valley Factory"); @@ -674,7 +683,7 @@ void shouldConvertGenericTypes() { Element.of("question1", true), Element.of("question2", false), Element.of("question3", List.of(Element.of("advanced", true), - Element.of("visible", "true"))) + Element.of("visible", "true"))) )); Form form = converter.toEntity(communication); @@ -856,6 +865,77 @@ void shouldConvertEntityFromColumnEntityWithArray() { } + @Test + void shouldConvertEntityFromRecordEntityWithColumnArray() { + + var effectiveJava = Book.builder() + .withId(10L) + .withName("Effective Java") + .withAge(2018 - Year.now().getValue()) + .build(); + var cleanCode = Book.builder() + .withId(1L) + .withName("Clen Code") + .withAge(2008 - Year.now().getValue()) + .build(); + + var bagBook = new BookBag("Max", + new Book[]{effectiveJava, cleanCode}); + + var entity = converter.toCommunication(bagBook); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entity).isNotNull(); + softly.assertThat(entity.name()).isEqualTo(BookBag.class.getSimpleName()); + softly.assertThat(entity.size()).isEqualTo(2); + softly.assertThat(entity.find("_id").orElseThrow().get()).isEqualTo(bagBook.owner()); + + var books = entity.find("books", new TypeReference>() { + }).orElseThrow(); + + softly.assertThat(books) + .hasSize(2); + + BiConsumer itemsAssertions = (actualBook, expectedBook) -> { + + softly.assertThat(actualBook.find("_id")) + .as("should found the entity's _id field") + .isPresent() + .get() + .as("invalid field type of the entity's _id field") + .isInstanceOf(Element.class) + .extracting(Element::get) + .as("invalid Book's id") + .isEqualTo(expectedBook.getId()); + + softly.assertThat(actualBook.find("name")) + .as("should found the entity's name field") + .isPresent() + .get() + .as("invalid field type of the entity's name field") + .isInstanceOf(Element.class) + .extracting(Element::get) + .as("invalid Book's name") + .isEqualTo(expectedBook.getName()); + + softly.assertThat(actualBook.find("age")) + .as("should found the entity's age field") + .isPresent() + .get() + .as("invalid field type of the entity's age field") + .isInstanceOf(Element.class) + .extracting(Element::get) + .as("invalid Book's age") + .isEqualTo(expectedBook.getAge()); + }; + + itemsAssertions.accept(CommunicationEntity.of("effectiveJava", books.get(0)), effectiveJava); + itemsAssertions.accept(CommunicationEntity.of("cleanCode", books.get(1)), cleanCode); + + }); + + } + private Object getValue(Optional column) { return column.map(Element::value).map(Value::get).orElse(null); diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/entities/constructor/BookBag.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/entities/constructor/BookBag.java new file mode 100644 index 000000000..e82298c13 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/semistructured/entities/constructor/BookBag.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 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.mapping.semistructured.entities.constructor; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import org.eclipse.jnosql.mapping.semistructured.entities.Book; + +@Entity +public record BookBag(@Id String owner, @Column Book[] books) { +}