From ea08f4a6c644f6267669134735f6b960e645ba5e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 26 Apr 2019 10:26:25 +0200 Subject: [PATCH] removed bloat-code from JsonComponents in DTOs --- build.gradle | 3 +- .../service/accessfilter/AccessMappings.java | 9 ++ .../accessfilter/JSonAccessFilter.java | 22 +-- ... JSonDeserializationWithAccessFilter.java} | 12 +- ...=> JSonSerializationWithAccessFilter.java} | 16 ++- .../JsonDeserializerWithAccessFilter.java | 24 ++++ .../JsonSerializerWithAccessFilter.java | 31 +++++ .../hsadminng/service/dto/CustomerDTO.java | 36 +---- .../service/util/ReflectionUtil.java | 86 ++++++++++-- ...erializationWithAccessFilterUnitTest.java} | 30 ++-- ...erializationWithAccessFilterUnitTest.java} | 28 ++-- .../service/dto/MembershipDTOUnitTest.java | 6 +- .../service/dto/ShareDTOUnitTest.java | 12 +- .../service/util/ReflectionUtilTest.java | 129 ++++++++++++++++++ 14 files changed, 337 insertions(+), 107 deletions(-) create mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessMappings.java rename src/main/java/org/hostsharing/hsadminng/service/accessfilter/{JSonDeserializerWithAccessFilter.java => JSonDeserializationWithAccessFilter.java} (91%) rename src/main/java/org/hostsharing/hsadminng/service/accessfilter/{JSonSerializerWithAccessFilter.java => JSonSerializationWithAccessFilter.java} (82%) create mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonDeserializerWithAccessFilter.java create mode 100644 src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonSerializerWithAccessFilter.java rename src/test/java/org/hostsharing/hsadminng/service/accessfilter/{JSonDeserializerWithAccessFilterUnitTest.java => JSonDeserializationWithAccessFilterUnitTest.java} (89%) rename src/test/java/org/hostsharing/hsadminng/service/accessfilter/{JSonSerializerWithAccessFilterUnitTest.java => JSonSerializationWithAccessFilterUnitTest.java} (80%) create mode 100644 src/test/java/org/hostsharing/hsadminng/service/util/ReflectionUtilTest.java diff --git a/build.gradle b/build.gradle index 3e16aca0..2712ccee 100644 --- a/build.gradle +++ b/build.gradle @@ -156,7 +156,8 @@ def jhipsterGeneratedClassesWithLowCoverage = [ def specialExceptions = [ // lots of unreachable code due to error handling / verifications - 'org.hostsharing.hsadminng.service.accessfilter.JSonAccessFilter' + 'org.hostsharing.hsadminng.service.accessfilter.JSonAccessFilter', + 'org.hostsharing.hsadminng.service.util.ReflectionUtil' ] jacocoTestCoverageVerification { diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessMappings.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessMappings.java new file mode 100644 index 00000000..d28d91b3 --- /dev/null +++ b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/AccessMappings.java @@ -0,0 +1,9 @@ +package org.hostsharing.hsadminng.service.accessfilter; + +import java.io.Serializable; + +/** + * A marker interface for DTO classes which can be used by {@link JsonSerializerWithAccessFilter} and {@link JsonDeserializerWithAccessFilter}. + */ +public interface AccessMappings extends Serializable { +} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilter.java index e254b83c..273b64cc 100644 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilter.java +++ b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonAccessFilter.java @@ -10,8 +10,6 @@ import org.springframework.context.ApplicationContext; import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import static com.google.common.base.Verify.verify; @@ -67,28 +65,16 @@ abstract class JSonAccessFilter { final ParentId parentIdAnnot = parentIdField.getAnnotation(ParentId.class); final Class parentDtoLoader = parentIdAnnot.resolver(); - final Class parentDtoClass = getGenericClassParameter(parentDtoLoader); - final Long parentId = (Long) ReflectionUtil.getValue(dto, parentIdField); + final Class rawType = IdToDtoResolver.class; + + final Class parentDtoClass = ReflectionUtil.determineGenericInterfaceParameter(parentDtoLoader, rawType, 0); + final Long parentId = ReflectionUtil.getValue(dto, parentIdField); final Role roleOnParent = SecurityUtils.getLoginUserRoleFor(parentDtoClass, parentId); final Object parentEntity = loadDto(parentDtoLoader, parentId); return Role.broadest(baseRole, getLoginUserRoleOnAncestorOfDtoClassIfHigher(roleOnParent, parentEntity)); } - @SuppressWarnings("unchecked") - private Class getGenericClassParameter(Class parentDtoLoader) { - for (Type genericInterface : parentDtoLoader.getGenericInterfaces()) { - if (genericInterface instanceof ParameterizedType) { - final ParameterizedType parameterizedType = (ParameterizedType) genericInterface; - if (parameterizedType.getRawType()== IdToDtoResolver.class) { - return (Class) parameterizedType.getActualTypeArguments()[0]; - } - } - - } - throw new AssertionError(parentDtoLoader.getSimpleName() + " expected to implement " + IdToDtoResolver.class.getSimpleName() + "<...DTO>"); - } - @SuppressWarnings("unchecked") protected Object loadDto(final Class resolverClass, final Long id) { verify(id != null, "id must not be null for " + resolverClass.getSimpleName()); diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializerWithAccessFilter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilter.java similarity index 91% rename from src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializerWithAccessFilter.java rename to src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilter.java index 9265f8b7..4993429f 100644 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializerWithAccessFilter.java +++ b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilter.java @@ -18,12 +18,18 @@ import java.util.Set; import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked; -public class JSonDeserializerWithAccessFilter extends JSonAccessFilter { +/** Actual implementation of JSON deserialization, where {link JSonDeserializerWithAccessFilter} + * is a stateless bean, {@link JSonDeserializationWithAccessFilter} exists only during the actual + * deserialization and contains a deserialization state. + * + * @param DTO class to serialize + */ +public class JSonDeserializationWithAccessFilter extends JSonAccessFilter { private final TreeNode treeNode; private final Set writtenFields = new HashSet<>(); - public JSonDeserializerWithAccessFilter(final ApplicationContext ctx, final JsonParser jsonParser, final DeserializationContext deserializationContext, Class dtoClass) { + public JSonDeserializationWithAccessFilter(final ApplicationContext ctx, final JsonParser jsonParser, final DeserializationContext deserializationContext, Class dtoClass) { super(ctx, unchecked(dtoClass::newInstance)); this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser)); } @@ -113,7 +119,7 @@ public class JSonDeserializerWithAccessFilter extends JSonAccessFilter { } else if (Boolean.class.isAssignableFrom(field.getType()) || boolean.class.isAssignableFrom(field.getType())) { ReflectionUtil.setValue(dto, field, Boolean.valueOf(value.toString())); } else if (field.getType().isEnum()) { - ReflectionUtil.setValue(dto, field, Enum.valueOf((Class) field.getType(), value.toString())); + ReflectionUtil.setValue(dto, field, ReflectionUtil.asEnumValue(field.getType(), value)); } else if (LocalDate.class.isAssignableFrom(field.getType())) { ReflectionUtil.setValue(dto, field, LocalDate.parse(value.toString())); } else { diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializerWithAccessFilter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilter.java similarity index 82% rename from src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializerWithAccessFilter.java rename to src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilter.java index 625fc009..d3ac6a10 100644 --- a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializerWithAccessFilter.java +++ b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilter.java @@ -12,14 +12,20 @@ import java.lang.reflect.Field; import java.math.BigDecimal; import java.time.LocalDate; -public class JSonSerializerWithAccessFilter extends JSonAccessFilter { +/** Actual implementation of JSON serialization, where {link JsonSerializerWithAccessFilter} + * is a stateless bean, {@link JSonSerializationWithAccessFilter} exists only during the actual + * serialization and contains a serialization state. + * + * @param DTO class to serialize + */ +public class JSonSerializationWithAccessFilter extends JSonAccessFilter { private final JsonGenerator jsonGenerator; private final SerializerProvider serializerProvider; - public JSonSerializerWithAccessFilter(final ApplicationContext ctx, - final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider, - final T dto) { + public JSonSerializationWithAccessFilter(final ApplicationContext ctx, + final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final T dto) { super(ctx, dto); this.jsonGenerator = jsonGenerator; this.serializerProvider = serializerProvider; diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonDeserializerWithAccessFilter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonDeserializerWithAccessFilter.java new file mode 100644 index 00000000..1b926283 --- /dev/null +++ b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonDeserializerWithAccessFilter.java @@ -0,0 +1,24 @@ +package org.hostsharing.hsadminng.service.accessfilter; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import org.hostsharing.hsadminng.service.util.ReflectionUtil; +import org.springframework.context.ApplicationContext; + +public abstract class JsonDeserializerWithAccessFilter extends JsonDeserializer { + + private final ApplicationContext ctx; + + public JsonDeserializerWithAccessFilter(final ApplicationContext ctx) { + this.ctx = ctx; + } + + @Override + public T deserialize(final JsonParser jsonParser, + final DeserializationContext deserializationContext) { + + final Class dtoClass = ReflectionUtil.determineGenericClassParameter(this.getClass(), JsonDeserializerWithAccessFilter.class, 0); + return new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, deserializationContext, dtoClass).deserialize(); + } +} diff --git a/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonSerializerWithAccessFilter.java b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonSerializerWithAccessFilter.java new file mode 100644 index 00000000..57a11878 --- /dev/null +++ b/src/main/java/org/hostsharing/hsadminng/service/accessfilter/JsonSerializerWithAccessFilter.java @@ -0,0 +1,31 @@ +package org.hostsharing.hsadminng.service.accessfilter; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.springframework.context.ApplicationContext; + +import java.io.IOException; + +/** A base class for a Spring bean for JSON serialization with field-based access filters. + * Where {@link JSonSerializationWithAccessFilter} is the actual stateful implementation and + * it's instances only exist during the process of serialization, this class is a stateless just + * used for service and context injection. + * + * @param DTO class to serialize + */ +public abstract class JsonSerializerWithAccessFilter extends JsonSerializer { + + protected final ApplicationContext ctx; + + public JsonSerializerWithAccessFilter(final ApplicationContext ctx) { + this.ctx = ctx; + } + + @Override + public void serialize(final T dto, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException { + + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, serializerProvider, dto).serialize(); + } +} diff --git a/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerDTO.java b/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerDTO.java index 4b9708d1..c1b42b75 100644 --- a/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerDTO.java +++ b/src/main/java/org/hostsharing/hsadminng/service/dto/CustomerDTO.java @@ -1,11 +1,5 @@ package org.hostsharing.hsadminng.service.dto; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; import org.hostsharing.hsadminng.domain.enumeration.CustomerKind; import org.hostsharing.hsadminng.domain.enumeration.VatRegion; import org.hostsharing.hsadminng.service.CustomerService; @@ -14,15 +8,13 @@ import org.springframework.boot.jackson.JsonComponent; import org.springframework.context.ApplicationContext; import javax.validation.constraints.*; -import java.io.IOException; -import java.io.Serializable; import java.time.LocalDate; import java.util.Objects; /** * A DTO for the Customer entity. */ -public class CustomerDTO extends FluentBuilder implements Serializable { +public class CustomerDTO extends FluentBuilder implements AccessMappings { @SelfId(resolver = CustomerService.class) @AccessFor(read = Role.ANY_CUSTOMER_USER) @@ -265,36 +257,18 @@ public class CustomerDTO extends FluentBuilder implements Serializa } @JsonComponent - public static class CustomerJsonSerializer extends JsonSerializer { - - private final ApplicationContext ctx; + public static class CustomerJsonSerializer extends JsonSerializerWithAccessFilter { public CustomerJsonSerializer(final ApplicationContext ctx) { - this.ctx = ctx; - } - - @Override - public void serialize(final CustomerDTO customerDTO, final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider) throws IOException { - - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, serializerProvider, customerDTO).serialize(); + super(ctx); } } @JsonComponent - public static class CustomerJsonDeserializer extends JsonDeserializer { - - private final ApplicationContext ctx; + public static class CustomerJsonDeserializer extends JsonDeserializerWithAccessFilter { public CustomerJsonDeserializer(final ApplicationContext ctx) { - this.ctx = ctx; - } - - @Override - public CustomerDTO deserialize(final JsonParser jsonParser, - final DeserializationContext deserializationContext) { - - return new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, deserializationContext, CustomerDTO.class).deserialize(); + super(ctx); } } } diff --git a/src/main/java/org/hostsharing/hsadminng/service/util/ReflectionUtil.java b/src/main/java/org/hostsharing/hsadminng/service/util/ReflectionUtil.java index 377f2748..1a8dd358 100644 --- a/src/main/java/org/hostsharing/hsadminng/service/util/ReflectionUtil.java +++ b/src/main/java/org/hostsharing/hsadminng/service/util/ReflectionUtil.java @@ -1,16 +1,19 @@ package org.hostsharing.hsadminng.service.util; import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; public class ReflectionUtil { - public static void setValue(final T dto, final String fieldName, final Object value) { + public static Field getField(final Class aClass, final String fieldName) { try { - final Field field = dto.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - field.set(dto, value); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); + return aClass.getDeclaredField(fieldName); + } catch (final NoSuchFieldException e) { + if (aClass.getSuperclass() != Object.class) { + return getField(aClass.getSuperclass(), fieldName); + } + throw new IllegalArgumentException(e); } } @@ -18,20 +21,81 @@ public class ReflectionUtil { try { field.setAccessible(true); field.set(dto, value); - } catch (IllegalAccessException e) { + } catch (final IllegalAccessException e) { throw new RuntimeException(e); } } - public static T getValue(final T dto, final Field field) { + @SuppressWarnings("unchecked") + public static R getValue(final T dto, final Field field) { try { field.setAccessible(true); - return (T) field.get(dto); - } catch (IllegalAccessException e) { + return (R) field.get(dto); + } catch (final IllegalAccessException e) { throw new RuntimeException(e); } } + /** + * Searches the annotations of 'clazz' for an implemented interface 'rawInterface' and returns the class of the actual generics parameter at the specified index. + * + * @param clazz a class which implements the generic interface 'rawInterface' + * @param rawInterface a generic interface + * @param paramIndex the index of the generics parameter within 'rawInterface' + * @param the expected class of the generics parameter at position 'index' in 'rawInterface' + * @return the actual generics parameter + */ + @SuppressWarnings("unchecked") + public static Class determineGenericInterfaceParameter(final Class clazz, final Class rawInterface, final int paramIndex) { + for (Type genericInterface : clazz.getGenericInterfaces()) { + if (genericInterface instanceof ParameterizedType) { + final ParameterizedType parameterizedType = (ParameterizedType) genericInterface; + if (parameterizedType.getRawType() == rawInterface) { + return (Class) parameterizedType.getActualTypeArguments()[paramIndex]; + } + } + } + if (clazz.getSuperclass() != Object.class) { + return determineGenericInterfaceParameter(clazz.getSuperclass(), rawInterface, paramIndex); + } + for (Class implementedInterface : clazz.getInterfaces()) { + final Class found = determineGenericInterfaceParameter(implementedInterface, rawInterface, paramIndex); + if (found != null) { + return found; + } + } + throw new AssertionError(clazz.getSimpleName() + " expected to implement " + rawInterface.getSimpleName() + "<...>"); + } + + /** + * Searches the annotations of 'clazz' for an extended class 'rawClass' and returns the class of the actual generics parameter at the specified index. + * + * @param clazz a class which implements the generic interface 'rawClass' + * @param rawClass a generic class + * @param paramIndex the index of the generics parameter within 'rawClass' + * @param the expected class of the generics parameter at position 'index' in 'rawClass' + * @return the actual generics parameter + */ + @SuppressWarnings("unchecked") + public static Class determineGenericClassParameter(final Class clazz, final Class rawClass, final int paramIndex) { + final Type genericClass = clazz.getGenericSuperclass(); + if (genericClass instanceof ParameterizedType) { + final ParameterizedType parameterizedType = (ParameterizedType) genericClass; + if (parameterizedType.getRawType() == rawClass) { + return (Class) parameterizedType.getActualTypeArguments()[paramIndex]; + } + } + if (clazz.getSuperclass() != Object.class) { + return determineGenericClassParameter(clazz.getSuperclass(), rawClass, paramIndex); + } + throw new AssertionError(clazz.getSimpleName() + " expected to extend " + rawClass.getSimpleName() + "<...>"); + } + + @SuppressWarnings("unchecked") + public static > Enum asEnumValue(final Class type, final Object value) { + return Enum.valueOf((Class) type, value.toString()); + } + @FunctionalInterface public interface ThrowingSupplier { T get() throws Exception; @@ -40,7 +104,7 @@ public class ReflectionUtil { public static T unchecked(final ThrowingSupplier supplier) { try { return supplier.get(); - } catch (Exception e) { + } catch (final Exception e) { throw new RuntimeException(e); } } diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializerWithAccessFilterUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilterUnitTest.java similarity index 89% rename from src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializerWithAccessFilterUnitTest.java rename to src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilterUnitTest.java index 148eef9a..0db7844a 100644 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializerWithAccessFilterUnitTest.java +++ b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonDeserializationWithAccessFilterUnitTest.java @@ -32,7 +32,7 @@ import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext import static org.mockito.BDDMockito.given; @SuppressWarnings("ALL") -public class JSonDeserializerWithAccessFilterUnitTest { +public class JSonDeserializationWithAccessFilterUnitTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -98,7 +98,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ImmutablePair.of("openStringField", null))); // when - GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); + GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); // then assertThat(actualDto.openStringField).isNull(); @@ -113,7 +113,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ImmutablePair.of("openStringField", "String Value"))); // when - GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); + GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); // then assertThat(actualDto.openStringField).isEqualTo("String Value"); @@ -128,7 +128,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ImmutablePair.of("openIntegerField", 1234))); // when - GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); + GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); // then assertThat(actualDto.openIntegerField).isEqualTo(1234); @@ -157,7 +157,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ); // when - GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); + GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); // then assertThat(actualDto.openIntegerField).isEqualTo(11); @@ -183,7 +183,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); // then assertThat(exception).isInstanceOf(NotImplementedException.class); @@ -200,7 +200,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ImmutablePair.of("restrictedField", "update value of restricted field"))); // when - GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); + GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); // then assertThat(actualDto.restrictedField).isEqualTo("update value of restricted field"); @@ -217,7 +217,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ImmutablePair.of("restrictedField", "initial value of restricted field"))); // when - GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); + GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); // then assertThat(actualDto.restrictedField).isEqualTo("initial value of restricted field"); @@ -234,7 +234,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); // then assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { @@ -254,7 +254,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); // then assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { @@ -273,7 +273,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize()); // then assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { @@ -292,7 +292,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ); // when - final GivenChildDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize(); + final GivenChildDto actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize(); // then assertThat(actualDto.parentId).isEqualTo(1234L); @@ -309,7 +309,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { ImmutablePair.of("restrictedField", "Restricted String Value"))); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); // then assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { @@ -324,7 +324,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { givenJSonTree(asJSon(ImmutablePair.of("id", 1111L))); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDtoWithMultipleSelfId.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDtoWithMultipleSelfId.class).deserialize()); // then assertThat(exception).isInstanceOf(AssertionError.class).hasMessage("multiple @SelfId detected in GivenDtoWithMultipleSelfId"); @@ -338,7 +338,7 @@ public class JSonDeserializerWithAccessFilterUnitTest { givenJSonTree(asJSon(ImmutablePair.of("unknown", new Arbitrary()))); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDtoWithUnknownFieldType.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, GivenDtoWithUnknownFieldType.class).deserialize()); // then assertThat(exception).isInstanceOf(NotImplementedException.class) diff --git a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializerWithAccessFilterUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilterUnitTest.java similarity index 80% rename from src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializerWithAccessFilterUnitTest.java rename to src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilterUnitTest.java index b46da560..4841d729 100644 --- a/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializerWithAccessFilterUnitTest.java +++ b/src/test/java/org/hostsharing/hsadminng/service/accessfilter/JSonSerializationWithAccessFilterUnitTest.java @@ -21,7 +21,7 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -public class JSonSerializerWithAccessFilterUnitTest { +public class JSonSerializationWithAccessFilterUnitTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -55,7 +55,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializeStringField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeStringField("openStringField", givenDTO.openStringField); @@ -64,7 +64,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializeIntegerField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeNumberField("openIntegerField", givenDTO.openIntegerField); @@ -73,7 +73,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializePrimitiveIntField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeNumberField("openPrimitiveIntField", givenDTO.openPrimitiveIntField); @@ -82,7 +82,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializeLongField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeNumberField("openLongField", givenDTO.openLongField); @@ -91,7 +91,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializePrimitiveLongField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeNumberField("openPrimitiveLongField", givenDTO.openPrimitiveLongField); @@ -100,7 +100,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializeBooleanField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeBooleanField("openBooleanField", givenDTO.openBooleanField); @@ -109,7 +109,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializePrimitiveBooleanField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeBooleanField("openPrimitiveBooleanField", givenDTO.openPrimitiveBooleanField); @@ -118,7 +118,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializeBigDecimalField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeNumberField("openBigDecimalField", givenDTO.openBigDecimalField); @@ -127,7 +127,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializeLocalDateField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeStringField("openLocalDateField", givenDTO.openLocalDateFieldAsString); @@ -136,7 +136,7 @@ public class JSonSerializerWithAccessFilterUnitTest { @Test public void shouldSerializeEnumField() throws IOException { // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeStringField("openEnumField", givenDTO.openEnumFieldAsString); @@ -150,7 +150,7 @@ public class JSonSerializerWithAccessFilterUnitTest { MockSecurityContext.givenUserHavingRole(GivenCustomerDto.class, 888L, Role.FINANCIAL_CONTACT); // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeStringField("restrictedField", givenDTO.restrictedField); @@ -164,7 +164,7 @@ public class JSonSerializerWithAccessFilterUnitTest { MockSecurityContext.givenUserHavingRole(GivenCustomerDto.class, 888L, Role.ANY_CUSTOMER_USER); // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator, never()).writeStringField("restrictedField", givenDTO.restrictedField); @@ -183,7 +183,7 @@ public class JSonSerializerWithAccessFilterUnitTest { final GivenDtoWithUnimplementedFieldType givenDtoWithUnimplementedFieldType = new GivenDtoWithUnimplementedFieldType(); // when - final Throwable actual = catchThrowable(() -> new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDtoWithUnimplementedFieldType).serialize()); + final Throwable actual = catchThrowable(() -> new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDtoWithUnimplementedFieldType).serialize()); // then assertThat(actual).isInstanceOf(NotImplementedException.class); diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOUnitTest.java index 90171bed..da6e4131 100644 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOUnitTest.java +++ b/src/test/java/org/hostsharing/hsadminng/service/dto/MembershipDTOUnitTest.java @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.tuple.ImmutablePair; import org.hostsharing.hsadminng.service.CustomerService; import org.hostsharing.hsadminng.service.MembershipService; -import org.hostsharing.hsadminng.service.accessfilter.JSonDeserializerWithAccessFilter; +import org.hostsharing.hsadminng.service.accessfilter.JSonDeserializationWithAccessFilter; import org.hostsharing.hsadminng.service.accessfilter.Role; import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; import org.junit.Before; @@ -74,7 +74,7 @@ public class MembershipDTOUnitTest { givenJSonTree(asJSon(ImmutablePair.of("customerId", 1234L))); // when - final MembershipDTO actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, MembershipDTO.class).deserialize(); + final MembershipDTO actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, MembershipDTO.class).deserialize(); // then assertThat(actualDto.getCustomerId()).isEqualTo(1234L); @@ -87,7 +87,7 @@ public class MembershipDTOUnitTest { givenJSonTree(asJSon(ImmutablePair.of("customerId", 1234L))); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, MembershipDTO.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, MembershipDTO.class).deserialize()); // then assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { diff --git a/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOUnitTest.java b/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOUnitTest.java index b9346faf..3713d616 100644 --- a/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOUnitTest.java +++ b/src/test/java/org/hostsharing/hsadminng/service/dto/ShareDTOUnitTest.java @@ -9,8 +9,8 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.hostsharing.hsadminng.domain.enumeration.ShareAction; import org.hostsharing.hsadminng.service.CustomerService; import org.hostsharing.hsadminng.service.MembershipService; -import org.hostsharing.hsadminng.service.accessfilter.JSonDeserializerWithAccessFilter; -import org.hostsharing.hsadminng.service.accessfilter.JSonSerializerWithAccessFilter; +import org.hostsharing.hsadminng.service.accessfilter.JSonDeserializationWithAccessFilter; +import org.hostsharing.hsadminng.service.accessfilter.JSonSerializationWithAccessFilter; import org.hostsharing.hsadminng.service.accessfilter.Role; import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; import org.junit.Before; @@ -89,7 +89,7 @@ public class ShareDTOUnitTest { givenJSonTree(asJSon(ImmutablePair.of("membershipId", SOME_MEMBERSHIP_ID))); // when - final ShareDTO actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, ShareDTO.class).deserialize(); + final ShareDTO actualDto = new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, ShareDTO.class).deserialize(); // then assertThat(actualDto.getMembershipId()).isEqualTo(SOME_MEMBERSHIP_ID); @@ -102,7 +102,7 @@ public class ShareDTOUnitTest { givenJSonTree(asJSon(ImmutablePair.of("membershipId", ShareDTOUnitTest.SOME_MEMBERSHIP_ID))); // when - Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, ShareDTO.class).deserialize()); + Throwable exception = catchThrowable(() -> new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, null, ShareDTO.class).deserialize()); // then assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> { @@ -118,7 +118,7 @@ public class ShareDTOUnitTest { final ShareDTO givenDTO = createShareDto(); // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeNumberField("id", givenDTO.getId()); @@ -133,7 +133,7 @@ public class ShareDTOUnitTest { final ShareDTO givenDTO = createShareDto(); // when - new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); + new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, null, givenDTO).serialize(); // then verify(jsonGenerator).writeNumberField("id", givenDTO.getId()); diff --git a/src/test/java/org/hostsharing/hsadminng/service/util/ReflectionUtilTest.java b/src/test/java/org/hostsharing/hsadminng/service/util/ReflectionUtilTest.java new file mode 100644 index 00000000..c57c9901 --- /dev/null +++ b/src/test/java/org/hostsharing/hsadminng/service/util/ReflectionUtilTest.java @@ -0,0 +1,129 @@ +package org.hostsharing.hsadminng.service.util; + +import org.junit.Test; + +import java.awt.*; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.ThrowableAssert.catchThrowable; +import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked; + + +public class ReflectionUtilTest { + + @Test + public void setValue() { + final TestDto dto = new TestDto(5); + ReflectionUtil.setValue(dto, ReflectionUtil.getField(dto.getClass(), "intVal"), 77); + assertThat(dto.intVal).isEqualTo(77); + } + + @Test + public void setValueViaSuperclass() { + final SubTestDto dto = new SubTestDto(5); + ReflectionUtil.setValue(dto, ReflectionUtil.getField(dto.getClass(), "intVal"), 77); + assertThat(dto.intVal).isEqualTo(77); + } + + @Test + public void getValue() { + final TestDto dto = new TestDto(5); + final int actual = ReflectionUtil.getValue(dto, ReflectionUtil.getField(dto.getClass(), "intVal")); + assertThat(actual).isEqualTo(5); + } + + @Test + public void getValueViaSuperclass() { + final SubTestDto dto = new SubTestDto(5); + final int actual = ReflectionUtil.getValue(dto, ReflectionUtil.getField(dto.getClass(), "intVal")); + assertThat(actual).isEqualTo(5); + } + + @Test + public void determineGenericInterfaceParameteDirect() { + Class actual = ReflectionUtil.determineGenericInterfaceParameter(SuperClass.class, GenericInterface.class, 1); + assertThat(actual).isEqualTo(Long.class); + } + + @Test + public void determineGenericInterfaceParameterViaSuperclass() { + Class actual = ReflectionUtil.determineGenericInterfaceParameter(SomeClass.class, GenericInterface.class, 1); + assertThat(actual).isEqualTo(Long.class); + } + + @Test + public void throwsExceptionIfGenericInterfaceNotImplemented() { + final Throwable actual = catchThrowable(() -> ReflectionUtil.determineGenericInterfaceParameter(SomeClass.class, UnusedGenericInterface.class, 1)); + assertThat(actual).isInstanceOf(AssertionError.class).hasMessageContaining("GenericClass expected to implement UnusedGenericInterface<...>"); + } + + @Test + public void determineGenericClassParameterDirect() { + Class actual = ReflectionUtil.determineGenericClassParameter(SuperClass.class, GenericClass.class, 1); + assertThat(actual).isEqualTo(Boolean.class); + } + + @Test + public void determineGenericClassParameterViaSuperclss() { + Class actual = ReflectionUtil.determineGenericClassParameter(SomeClass.class, GenericClass.class, 1); + assertThat(actual).isEqualTo(Boolean.class); + } + + @Test + public void throwsExceptionIfGenericClassNotExended() { + final Throwable actual = catchThrowable(() -> ReflectionUtil.determineGenericClassParameter(SomeClass.class, UnusedSuperClass.class, 1)); + assertThat(actual).isInstanceOf(AssertionError.class).hasMessageContaining("GenericClass expected to extend UnusedSuperClass<...>"); + } + + @Test + public void uncheckedRethrowsCheckedException() { + final Exception givenException = new Exception("Checked Test Exception"); + final Throwable actual = catchThrowable(() -> unchecked(() -> { + throw givenException; + })); + assertThat(actual).isInstanceOfSatisfying(RuntimeException.class, rte -> + assertThat(rte.getCause()).isSameAs(givenException) + ); + } + + @Test + public void asEnumValue() { + assertThat(ReflectionUtil.asEnumValue(Color.class, "RED")).isEqualTo(Color.RED); + } + + // --- only test fixture below --- + + private static class TestDto { + int intVal; + + TestDto(final int intval) { + this.intVal = intval; + } + } + + private static class SubTestDto extends TestDto { + + SubTestDto(final int intval) { + super(intval); + } + } + + private static class SomeClass extends SuperClass { + } + + private static class SuperClass extends GenericClass implements GenericInterface { + } + + + private static class UnusedSuperClass extends GenericClass implements GenericInterface { + } + + private static class GenericClass { + } + + private interface GenericInterface { + } + + private interface UnusedGenericInterface { + } +}