diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/IntrospectionUtils.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/IntrospectionUtils.java index 1ba8b133d..6ae386962 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/IntrospectionUtils.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/IntrospectionUtils.java @@ -13,6 +13,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Spliterator; @@ -48,12 +49,12 @@ public static EntityIntrospectionResult introspect(Class entity) { * @param entity a Java entity class to introspect * @param propertyName the name of the property * @return true if property has Transient annotation or transient field modifier - * @throws RuntimeException if property does not exists + * @throws NoSuchElementException if property does not exists */ public static boolean isTransient(Class entity, String propertyName) { return introspect(entity).getPropertyDescriptor(propertyName) .map(AttributePropertyDescriptor::isTransient) - .orElseThrow(() -> new RuntimeException(new NoSuchFieldException(propertyName))); + .orElseThrow(() -> new NoSuchElementException(propertyName)); } /** @@ -62,9 +63,9 @@ public static boolean isTransient(Class entity, String propertyName) { * @param entity a Java entity class to introspect * @param propertyName the name of the property * @return true if property is persitent - * @throws RuntimeException if property does not exists + * @throws NoSuchElementException if property does not exists */ - public static boolean isPesistent(Class entity, String propertyName) { + public static boolean isPersistent(Class entity, String propertyName) { return !isTransient(entity, propertyName); } @@ -74,12 +75,12 @@ public static boolean isPesistent(Class entity, String propertyName) { * @param entity a Java entity class to introspect * @param propertyName the name of the property * @return true if property has GraphQLIgnore annotation - * @throws RuntimeException if property does not exists + * @throws NoSuchElementException if property does not exists */ public static boolean isIgnored(Class entity, String propertyName) { return introspect(entity).getPropertyDescriptor(propertyName) .map(AttributePropertyDescriptor::isIgnored) - .orElseThrow(() -> new RuntimeException(new NoSuchFieldException(propertyName))); + .orElseThrow(() -> new NoSuchElementException(propertyName)); } /** @@ -88,7 +89,7 @@ public static boolean isIgnored(Class entity, String propertyName) { * @param entity a Java entity class to introspect * @param propertyName the name of the property * @return true if property has no GraphQLIgnore annotation - * @throws RuntimeException if property does not exists + * @throws NoSuchElementException if property does not exists */ public static boolean isNotIgnored(Class entity, String propertyName) { return !isIgnored(entity, propertyName); diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/CalculatedEntityTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/CalculatedEntityTests.java index a5c3031fa..604bb72a3 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/CalculatedEntityTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/CalculatedEntityTests.java @@ -84,6 +84,7 @@ public void testIgnoreFields() { " fieldMem" + " fieldFun" + " logic" + + " age" + " customLogic" + " hideField" + " hideFieldFunction" + diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/impl/IntrospectionUtilsTest.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/impl/IntrospectionUtilsTest.java index 3c23611ea..1321966cd 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/impl/IntrospectionUtilsTest.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/impl/IntrospectionUtilsTest.java @@ -1,236 +1,254 @@ -package com.introproventures.graphql.jpa.query.schema.impl; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.mockito.Mockito.when; - -import java.util.Optional; - -import javax.persistence.metamodel.Attribute; - -import org.junit.Test; -import org.mockito.Mockito; - -import com.introproventures.graphql.jpa.query.schema.impl.IntrospectionUtils.EntityIntrospectionResult; -import com.introproventures.graphql.jpa.query.schema.impl.IntrospectionUtils.EntityIntrospectionResult.AttributePropertyDescriptor; -import com.introproventures.graphql.jpa.query.schema.model.calculated.CalculatedEntity; -import com.introproventures.graphql.jpa.query.schema.model.calculated.ParentCalculatedEntity; - -public class IntrospectionUtilsTest { - - // given - private final Class entity = CalculatedEntity.class; - - @Test(expected = RuntimeException.class) - public void testIsTransientNonExisting() throws Exception { - // then - assertThat(IntrospectionUtils.isTransient(entity, "notFound")).isFalse(); - } - - @Test(expected = RuntimeException.class) - public void testIsIgnoredNonExisting() throws Exception { - // then - assertThat(IntrospectionUtils.isIgnored(entity, "notFound")).isFalse(); - } - - @Test - public void testIsTransientClass() throws Exception { - // then - assertThat(IntrospectionUtils.isTransient(entity, "class")).isFalse(); - } - - @Test - public void testIsTransientFunction() throws Exception { - // then - assertThat(IntrospectionUtils.isTransient(entity, "fieldFun")).isTrue(); - assertThat(IntrospectionUtils.isTransient(entity, "hideFieldFunction")).isFalse(); - } - - @Test - public void testIsPersistentFunction() throws Exception { - // then - assertThat(IntrospectionUtils.isPesistent(entity, "fieldFun")).isFalse(); - assertThat(IntrospectionUtils.isPesistent(entity, "hideFieldFunction")).isTrue(); - } - - @Test - public void testIsTransientFields() throws Exception { - // then - assertThat(IntrospectionUtils.isTransient(entity, "fieldFun")).isTrue(); - assertThat(IntrospectionUtils.isTransient(entity, "fieldMem")).isTrue(); - assertThat(IntrospectionUtils.isTransient(entity, "hideField")).isTrue(); - assertThat(IntrospectionUtils.isTransient(entity, "logic")).isTrue(); - assertThat(IntrospectionUtils.isTransient(entity, "transientModifier")).isTrue(); - assertThat(IntrospectionUtils.isTransient(entity, "parentTransientModifier")).isTrue(); - assertThat(IntrospectionUtils.isTransient(entity, "parentTransient")).isTrue(); - assertThat(IntrospectionUtils.isTransient(entity, "parentTransientGetter")).isTrue(); - } - - @Test - public void testNotTransientFields() throws Exception { - // then - assertThat(IntrospectionUtils.isTransient(entity, "id")).isFalse(); - assertThat(IntrospectionUtils.isTransient(entity, "info")).isFalse(); - assertThat(IntrospectionUtils.isTransient(entity, "title")).isFalse(); - assertThat(IntrospectionUtils.isTransient(entity, "parentField")).isFalse(); - } - - @Test - public void testByPassSetMethod() throws Exception { - // then - assertThat(IntrospectionUtils.isTransient(entity,"something")).isFalse(); - } - - @Test - public void shouldIgnoreMethodsThatAreAnnotatedWithGraphQLIgnore() { - //then - assertThat(IntrospectionUtils.isIgnored(entity, "propertyIgnoredOnGetter")).isTrue(); - assertThat(IntrospectionUtils.isIgnored(entity, "ignoredTransientValue")).isTrue(); - assertThat(IntrospectionUtils.isIgnored(entity, "hideField")).isTrue(); - assertThat(IntrospectionUtils.isIgnored(entity, "parentGraphQLIgnore")).isTrue(); - - assertThat(IntrospectionUtils.isIgnored(entity, "transientModifier")).isFalse(); - assertThat(IntrospectionUtils.isIgnored(entity, "parentTransientModifier")).isFalse(); - assertThat(IntrospectionUtils.isIgnored(entity, "parentTransient")).isFalse(); - assertThat(IntrospectionUtils.isIgnored(entity, "parentTransientGetter")).isFalse(); - } - - @Test - public void shouldNotIgnoreMethodsThatAreNotAnnotatedWithGraphQLIgnore() { - //then - assertThat(IntrospectionUtils.isNotIgnored(entity, "propertyIgnoredOnGetter")).isFalse(); - assertThat(IntrospectionUtils.isNotIgnored(entity, "ignoredTransientValue")).isFalse(); - assertThat(IntrospectionUtils.isNotIgnored(entity, "hideField")).isFalse(); - assertThat(IntrospectionUtils.isNotIgnored(entity, "parentGraphQLIgnore")).isFalse(); - - assertThat(IntrospectionUtils.isNotIgnored(entity, "transientModifier")).isTrue(); - assertThat(IntrospectionUtils.isNotIgnored(entity, "parentTransientModifier")).isTrue(); - assertThat(IntrospectionUtils.isNotIgnored(entity, "parentTransient")).isTrue(); - assertThat(IntrospectionUtils.isNotIgnored(entity, "parentTransientGetter")).isTrue(); - } - - @SuppressWarnings("rawtypes") - @Test - public void shouldGetClassesInHierarchy() { - //when - Class[] result = IntrospectionUtils.introspect(entity) - .getClasses() - .toArray(Class[]::new); - - //then - assertThat(result).containsExactly(CalculatedEntity.class, - ParentCalculatedEntity.class, - Object.class); - } - - @Test - public void testGetPropertyDescriptorsSchemaDescription() throws Exception { - // when - EntityIntrospectionResult result = IntrospectionUtils.introspect(CalculatedEntity.class); - - // then - assertThat(result.getPropertyDescriptors()) - .extracting(AttributePropertyDescriptor::getSchemaDescription) - .filteredOn(Optional::isPresent) - .extracting(Optional::get) - .containsOnly("title", - "transientModifier", - "i desc member", - "i desc function", - "getParentTransientGetter", - "parentTransientModifier", - "Uppercase", - "UppercaseGetter"); - } - - @Test - public void testGetPropertyDescriptorSchemaDescriptionByAttribute() throws Exception { - Attribute attribute = Mockito.mock(Attribute.class); - - when(attribute.getName()).thenReturn("title"); - - // when - Optional result = IntrospectionUtils.introspect(CalculatedEntity.class) - .getPropertyDescriptor(attribute); - // then - assertThat(result.isPresent()).isTrue(); - } - - @Test - public void testGetParentEntitySchemaDescription() throws Exception { - // when - EntityIntrospectionResult result = IntrospectionUtils.introspect(CalculatedEntity.class); - - // then - assertThat(result.getSchemaDescription()).contains("ParentCalculatedEntity description"); - assertThat(result.hasSchemaDescription()).isTrue(); - } - - @Test - public void testUppercasePropertyNamesAreSupported() throws Exception { - // when - EntityIntrospectionResult result = IntrospectionUtils.introspect(CalculatedEntity.class); - - // then - assertThat(result.getField("Uppercase")).isPresent(); - assertThat(result.getPropertyDescriptor("Uppercase")).isPresent(); - - assertThat(result.getPropertyDescriptor("Uppercase") - .get()) - .extracting(AttributePropertyDescriptor::isIgnored) - .isEqualTo(false); - - assertThat(result.getPropertyDescriptor("Uppercase") - .get()) - .extracting(AttributePropertyDescriptor::isTransient) - .isEqualTo(false); - - assertThat(result.getPropertyDescriptor("Uppercase") - .get() - .getSchemaDescription()) - .contains("Uppercase"); - - assertThat(result.getPropertyDescriptor("UppercaseGetter") - .get()) - .extracting(AttributePropertyDescriptor::isIgnored) - .isEqualTo(false); - - assertThat(result.getPropertyDescriptor("UppercaseGetter") - .get() - .getSchemaDescription()) - .contains("UppercaseGetter"); - - assertThat(result.getPropertyDescriptor("UppercaseGetter") - .get()) - .extracting(AttributePropertyDescriptor::isTransient) - .isEqualTo(true); - - assertThat(result.getPropertyDescriptor("UppercaseGetterIgnore") - .get()) - .extracting(AttributePropertyDescriptor::isIgnored) - .isEqualTo(true); - } - - @Test - public void shouldNotFailWhenPropertyIsDuplicatedInParentAndChild() { - // given - // There is a duplicated property in parent and child - - // then - assertThatCode(() -> IntrospectionUtils.introspect(CalculatedEntity.class)).doesNotThrowAnyException(); - } - - @Test - public void shouldCorrectlyIntrospectPropertyDuplicatedInParentAndChild() { - // given - // There is a duplicated property in parent and child - - // when - EntityIntrospectionResult introspectionResult = IntrospectionUtils.introspect(CalculatedEntity.class); - - // then - Optional propertyOverriddenInChild = introspectionResult.getPropertyDescriptor("propertyDuplicatedInChild"); - assertThat(propertyOverriddenInChild).isPresent(); - } - -} +package com.introproventures.graphql.jpa.query.schema.impl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.Mockito.when; + +import java.util.NoSuchElementException; +import java.util.Optional; + +import javax.persistence.metamodel.Attribute; + +import org.junit.Test; +import org.mockito.Mockito; + +import com.introproventures.graphql.jpa.query.schema.impl.IntrospectionUtils.EntityIntrospectionResult; +import com.introproventures.graphql.jpa.query.schema.impl.IntrospectionUtils.EntityIntrospectionResult.AttributePropertyDescriptor; +import com.introproventures.graphql.jpa.query.schema.model.calculated.CalculatedEntity; +import com.introproventures.graphql.jpa.query.schema.model.calculated.ParentCalculatedEntity; + +public class IntrospectionUtilsTest { + + // given + private final Class entity = CalculatedEntity.class; + + @Test(expected = NoSuchElementException.class) + public void testIsTransientNonExisting() throws Exception { + // then + assertThat(IntrospectionUtils.isTransient(entity, "notFound")).isFalse(); + } + + @Test(expected = NoSuchElementException.class) + public void testIsIgnoredNonExisting() throws Exception { + // then + assertThat(IntrospectionUtils.isIgnored(entity, "notFound")).isFalse(); + } + + @Test + public void testIsTransientClass() throws Exception { + // then + assertThat(IntrospectionUtils.isTransient(entity, "class")).isFalse(); + } + + @Test + public void testIsTransientFunction() throws Exception { + // then + assertThat(IntrospectionUtils.isTransient(entity, "fieldFun")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "hideFieldFunction")).isFalse(); + } + + @Test + public void testIsPersistentFunction() throws Exception { + // then + assertThat(IntrospectionUtils.isPersistent(entity, "fieldFun")).isFalse(); + assertThat(IntrospectionUtils.isPersistent(entity, "hideFieldFunction")).isTrue(); + } + + @Test + public void testIsTransientFields() throws Exception { + // then + assertThat(IntrospectionUtils.isTransient(entity, "fieldFun")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "fieldMem")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "hideField")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "logic")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "transientModifier")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "parentTransientModifier")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "parentTransient")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "parentTransientGetter")).isTrue(); + } + + @Test + public void testNotTransientFields() throws Exception { + // then + assertThat(IntrospectionUtils.isTransient(entity, "id")).isFalse(); + assertThat(IntrospectionUtils.isTransient(entity, "info")).isFalse(); + assertThat(IntrospectionUtils.isTransient(entity, "title")).isFalse(); + assertThat(IntrospectionUtils.isTransient(entity, "parentField")).isFalse(); + } + + @Test + public void testByPassSetMethod() throws Exception { + // then + assertThat(IntrospectionUtils.isTransient(entity,"something")).isFalse(); + } + + @Test + public void shouldIgnoreMethodsThatAreAnnotatedWithGraphQLIgnore() { + //then + assertThat(IntrospectionUtils.isIgnored(entity, "propertyIgnoredOnGetter")).isTrue(); + assertThat(IntrospectionUtils.isIgnored(entity, "ignoredTransientValue")).isTrue(); + assertThat(IntrospectionUtils.isIgnored(entity, "hideField")).isTrue(); + assertThat(IntrospectionUtils.isIgnored(entity, "parentGraphQLIgnore")).isTrue(); + + assertThat(IntrospectionUtils.isIgnored(entity, "transientModifier")).isFalse(); + assertThat(IntrospectionUtils.isIgnored(entity, "parentTransientModifier")).isFalse(); + assertThat(IntrospectionUtils.isIgnored(entity, "parentTransient")).isFalse(); + assertThat(IntrospectionUtils.isIgnored(entity, "parentTransientGetter")).isFalse(); + } + + @Test + public void shouldNotIgnoreMethodsThatAreNotAnnotatedWithGraphQLIgnore() { + //then + assertThat(IntrospectionUtils.isNotIgnored(entity, "propertyIgnoredOnGetter")).isFalse(); + assertThat(IntrospectionUtils.isNotIgnored(entity, "ignoredTransientValue")).isFalse(); + assertThat(IntrospectionUtils.isNotIgnored(entity, "hideField")).isFalse(); + assertThat(IntrospectionUtils.isNotIgnored(entity, "parentGraphQLIgnore")).isFalse(); + + assertThat(IntrospectionUtils.isNotIgnored(entity, "transientModifier")).isTrue(); + assertThat(IntrospectionUtils.isNotIgnored(entity, "parentTransientModifier")).isTrue(); + assertThat(IntrospectionUtils.isNotIgnored(entity, "parentTransient")).isTrue(); + assertThat(IntrospectionUtils.isNotIgnored(entity, "parentTransientGetter")).isTrue(); + } + + @SuppressWarnings("rawtypes") + @Test + public void shouldGetClassesInHierarchy() { + //when + Class[] result = IntrospectionUtils.introspect(entity) + .getClasses() + .toArray(Class[]::new); + + //then + assertThat(result).containsExactly(CalculatedEntity.class, + ParentCalculatedEntity.class, + Object.class); + } + + @Test + public void testGetPropertyDescriptorsSchemaDescription() throws Exception { + // when + EntityIntrospectionResult result = IntrospectionUtils.introspect(CalculatedEntity.class); + + // then + assertThat(result.getPropertyDescriptors()) + .extracting(AttributePropertyDescriptor::getSchemaDescription) + .filteredOn(Optional::isPresent) + .extracting(Optional::get) + .containsOnly("title", + "transientModifier", + "i desc member", + "i desc function", + "getParentTransientGetter", + "parentTransientModifier", + "Uppercase", + "UppercaseGetter"); + } + + @Test + public void testGetPropertyDescriptorSchemaDescriptionByAttribute() throws Exception { + Attribute attribute = Mockito.mock(Attribute.class); + + when(attribute.getName()).thenReturn("title"); + + // when + Optional result = IntrospectionUtils.introspect(CalculatedEntity.class) + .getPropertyDescriptor(attribute); + // then + assertThat(result.isPresent()).isTrue(); + } + + @Test + public void testGetParentEntitySchemaDescription() throws Exception { + // when + EntityIntrospectionResult result = IntrospectionUtils.introspect(CalculatedEntity.class); + + // then + assertThat(result.getSchemaDescription()).contains("ParentCalculatedEntity description"); + assertThat(result.hasSchemaDescription()).isTrue(); + } + + @Test + public void testUppercasePropertyNamesAreSupported() throws Exception { + // when + EntityIntrospectionResult result = IntrospectionUtils.introspect(CalculatedEntity.class); + + // then + assertThat(result.getField("Uppercase")).isPresent(); + assertThat(result.getPropertyDescriptor("Uppercase")).isPresent(); + + assertThat(result.getPropertyDescriptor("Uppercase") + .get()) + .extracting(AttributePropertyDescriptor::isIgnored) + .isEqualTo(false); + + assertThat(result.getPropertyDescriptor("Uppercase") + .get()) + .extracting(AttributePropertyDescriptor::isTransient) + .isEqualTo(false); + + assertThat(result.getPropertyDescriptor("Uppercase") + .get() + .getSchemaDescription()) + .contains("Uppercase"); + + assertThat(result.getPropertyDescriptor("UppercaseGetter") + .get()) + .extracting(AttributePropertyDescriptor::isIgnored) + .isEqualTo(false); + + assertThat(result.getPropertyDescriptor("UppercaseGetter") + .get() + .getSchemaDescription()) + .contains("UppercaseGetter"); + + assertThat(result.getPropertyDescriptor("UppercaseGetter") + .get()) + .extracting(AttributePropertyDescriptor::isTransient) + .isEqualTo(true); + + assertThat(result.getPropertyDescriptor("UppercaseGetterIgnore") + .get()) + .extracting(AttributePropertyDescriptor::isIgnored) + .isEqualTo(true); + } + + @Test + public void testPrivateModifierOnGetterProperty() throws Exception { + // when + EntityIntrospectionResult result = IntrospectionUtils.introspect(CalculatedEntity.class); + + // then + assertThat(IntrospectionUtils.isIgnored(entity, "age")).isFalse(); + assertThat(IntrospectionUtils.isPersistent(entity, "age")).isTrue(); + assertThat(IntrospectionUtils.isTransient(entity, "age")).isFalse(); + + assertThat(result.getField("age")).isPresent(); + assertThat(result.getPropertyDescriptor("age")).isPresent(); + assertThat(result.getPropertyDescriptor("age") + .get() + .getReadMethod()) + .isEmpty(); + } + + @Test + public void shouldNotFailWhenPropertyIsDuplicatedInParentAndChild() { + // given + // There is a duplicated property in parent and child + + // then + assertThatCode(() -> IntrospectionUtils.introspect(CalculatedEntity.class)).doesNotThrowAnyException(); + } + + @Test + public void shouldCorrectlyIntrospectPropertyDuplicatedInParentAndChild() { + // given + // There is a duplicated property in parent and child + + // when + EntityIntrospectionResult introspectionResult = IntrospectionUtils.introspect(CalculatedEntity.class); + + // then + Optional propertyOverriddenInChild = introspectionResult.getPropertyDescriptor("propertyDuplicatedInChild"); + assertThat(propertyOverriddenInChild).isPresent(); + } +} diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/model/calculated/CalculatedEntity.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/model/calculated/CalculatedEntity.java index 82106f0ca..b58f09d7d 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/model/calculated/CalculatedEntity.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/model/calculated/CalculatedEntity.java @@ -1,5 +1,8 @@ package com.introproventures.graphql.jpa.query.schema.model.calculated; +import java.time.LocalDate; +import java.time.Period; + import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Transient; @@ -54,7 +57,15 @@ public class CalculatedEntity extends ParentCalculatedEntity { @GraphQLDescription("Uppercase") String Uppercase; - + + private Integer age; + + private Integer getAge(){ + return Period.between(LocalDate.now(), + LocalDate.of(2000, 1, 1)) + .getYears(); + } + String UppercaseGetter; @GraphQLDescription("transientModifier")