merging current DTO during deserialization

This commit is contained in:
Michael Hoennig 2019-04-25 12:22:45 +02:00
parent fb961ca4a1
commit 3e30cf2d17
32 changed files with 431 additions and 163 deletions

View File

@ -80,7 +80,6 @@ public final class SecurityUtils {
} }
public static Role getLoginUserRoleFor(final Class<?> onDtoClass, final Long onId) { public static Role getLoginUserRoleFor(final Class<?> onDtoClass, final Long onId) {
final Role highestRole = userRoleAssignments.stream(). final Role highestRole = userRoleAssignments.stream().
map(ura -> map(ura ->
matches(onDtoClass, onId, ura) matches(onDtoClass, onId, ura)
@ -91,7 +90,7 @@ public final class SecurityUtils {
} }
private static boolean matches(Class<?> onDtoClass, Long onId, UserRoleAssignment ura) { private static boolean matches(Class<?> onDtoClass, Long onId, UserRoleAssignment ura) {
final boolean matches = (ura.onClass == null || onDtoClass == ura.onClass) && (ura.onId == null || onId.equals(ura.onId) ); final boolean matches = (ura.onClass == null || onDtoClass == ura.onClass) && (ura.onId == null || ura.onId.equals(onId));
return matches; return matches;
} }

View File

@ -3,5 +3,5 @@ package org.hostsharing.hsadminng.service;
import java.util.Optional; import java.util.Optional;
public interface IdToDtoResolver<T> { public interface IdToDtoResolver<T> {
Optional<? extends T> findOne(Long id); Optional<T> findOne(Long id);
} }

View File

@ -19,7 +19,7 @@ import java.util.Optional;
*/ */
@Service @Service
@Transactional @Transactional
public class ShareService { public class ShareService implements IdToDtoResolver<ShareDTO> {
private final Logger log = LoggerFactory.getLogger(ShareService.class); private final Logger log = LoggerFactory.getLogger(ShareService.class);

View File

@ -4,14 +4,17 @@ import org.hostsharing.hsadminng.security.SecurityUtils;
import org.hostsharing.hsadminng.service.IdToDtoResolver; import org.hostsharing.hsadminng.service.IdToDtoResolver;
import org.hostsharing.hsadminng.service.dto.MembershipDTO; import org.hostsharing.hsadminng.service.dto.MembershipDTO;
import org.hostsharing.hsadminng.service.util.ReflectionUtil; import org.hostsharing.hsadminng.service.util.ReflectionUtil;
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import javax.persistence.EntityNotFoundException;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import static com.google.common.base.Verify.verify;
abstract class JSonAccessFilter<T> { abstract class JSonAccessFilter<T> {
private final ApplicationContext ctx; private final ApplicationContext ctx;
final T dto; final T dto;
@ -72,7 +75,7 @@ abstract class JSonAccessFilter<T> {
final Long parentId = (Long) ReflectionUtil.getValue(dto, parentIdField); final Long parentId = (Long) ReflectionUtil.getValue(dto, parentIdField);
final Role roleOnParent = SecurityUtils.getLoginUserRoleFor(parentDtoClass, parentId); final Role roleOnParent = SecurityUtils.getLoginUserRoleFor(parentDtoClass, parentId);
final Object parentEntity = findParentDto(parentDtoLoader, parentId); final Object parentEntity = loadDto(parentDtoLoader, parentId);
return Role.broadest(baseRole, getLoginUserRoleOnAncestorOfDtoClassIfHigher(roleOnParent, parentEntity)); return Role.broadest(baseRole, getLoginUserRoleOnAncestorOfDtoClassIfHigher(roleOnParent, parentEntity));
} }
@ -91,9 +94,16 @@ abstract class JSonAccessFilter<T> {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Object findParentDto(final Class<? extends IdToDtoResolver> parentDtoLoader, final Long parentId) { protected Object loadDto(final Class<? extends IdToDtoResolver> resolverClass, final Long id) {
final IdToDtoResolver<MembershipDTO> idToDtoResolver = ctx.getAutowireCapableBeanFactory().createBean(parentDtoLoader); verify(id != null, "id must not be null");
return idToDtoResolver.findOne(parentId).orElseThrow(() -> new EntityNotFoundException("Can't resolve parent entity ID " + parentId + " via " + parentDtoLoader));
final AutowireCapableBeanFactory beanFactory = ctx.getAutowireCapableBeanFactory();
verify(beanFactory != null, "no bean factory found, probably missing mock configuration for ApplicationContext, e.g. given(...)");
final IdToDtoResolver<MembershipDTO> resolverBean = beanFactory.createBean(resolverClass);
verify(resolverBean != null, "no " + resolverClass.getSimpleName() + " bean created, probably missing mock configuration for AutowireCapableBeanFactory, e.g. given(...)");
return resolverBean.findOne(id).orElseThrow(() -> new BadRequestAlertException("Can't resolve entity ID " + id + " via " + resolverClass, resolverClass.getSimpleName(), "isNotFound"));
} }
private static Field determineFieldWithAnnotation(final Class<?> dtoClass, final Class<? extends Annotation> idAnnotationClass) { private static Field determineFieldWithAnnotation(final Class<?> dtoClass, final Class<? extends Annotation> idAnnotationClass) {

View File

@ -3,15 +3,18 @@ package org.hostsharing.hsadminng.service.accessfilter;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.LongNode; import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.databind.node.TextNode;
import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.ObjectUtils;
import org.hostsharing.hsadminng.service.util.ReflectionUtil; import org.hostsharing.hsadminng.service.util.ReflectionUtil;
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.time.LocalDate;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -20,7 +23,7 @@ import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> { public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
private final TreeNode treeNode; private final TreeNode treeNode;
private final Set<Field> modifiedFields = new HashSet<>(); private final Set<Field> writtenFields = new HashSet<>();
public JSonDeserializerWithAccessFilter(final ApplicationContext ctx, final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) { public JSonDeserializerWithAccessFilter(final ApplicationContext ctx, final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) {
super(ctx, unchecked(dtoClass::newInstance)); super(ctx, unchecked(dtoClass::newInstance));
@ -30,7 +33,9 @@ public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
// Jackson deserializes from the JsonParser, thus no input parameter needed. // Jackson deserializes from the JsonParser, thus no input parameter needed.
public T deserialize() { public T deserialize() {
deserializeValues(); deserializeValues();
checkAccessToModifiedFields(); final T currentDto = loadCurrentDto(getId());
overwriteUnmodifiedFieldsWithCurrentValues(currentDto);
checkAccessToWrittenFields(currentDto);
return dto; return dto;
} }
@ -38,25 +43,52 @@ public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
treeNode.fieldNames().forEachRemaining(fieldName -> { treeNode.fieldNames().forEachRemaining(fieldName -> {
try { try {
final Field field = dto.getClass().getDeclaredField(fieldName); final Field field = dto.getClass().getDeclaredField(fieldName);
final Object value = readValue(treeNode, field); final Object newValue = readValue(treeNode, field);
writeValue(dto, field, value); writeValue(dto, field, newValue);
markAsModified(field);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
throw new RuntimeException("setting field " + fieldName + " failed", e); throw new RuntimeException("setting field " + fieldName + " failed", e);
} }
}); });
} }
@SuppressWarnings("unchecked")
private T loadCurrentDto(final Long id) {
if (id != null) {
return (T) loadDto(selfIdField.getAnnotation(SelfId.class).resolver(), id);
}
return null;
}
private void overwriteUnmodifiedFieldsWithCurrentValues(final Object currentDto) {
if ( currentDto == null ) {
return;
}
for (Field field : currentDto.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(AccessFor.class) && !writtenFields.contains(field)) {
final Object value = ReflectionUtil.getValue(currentDto, field);
ReflectionUtil.setValue(dto, field, value);
}
}
}
private Object readValue(final TreeNode treeNode, final Field field) { private Object readValue(final TreeNode treeNode, final Field field) {
final TreeNode fieldNode = treeNode.get(field.getName()); return readValue(treeNode, field.getName(), field.getType());
}
private Object readValue(final TreeNode treeNode, final String fieldName, final Class<?> fieldClass) {
final TreeNode fieldNode = treeNode.get(fieldName);
if (fieldNode instanceof TextNode) { if (fieldNode instanceof TextNode) {
return ((TextNode) fieldNode).asText(); return ((TextNode) fieldNode).asText();
} else if (fieldNode instanceof IntNode) { } else if (fieldNode instanceof IntNode) {
return ((IntNode) fieldNode).asInt(); return ((IntNode) fieldNode).asInt();
} else if (fieldNode instanceof LongNode) { } else if (fieldNode instanceof LongNode) {
return ((LongNode) fieldNode).asLong(); return ((LongNode) fieldNode).asLong();
} else if (fieldNode instanceof ArrayNode && LocalDate.class.isAssignableFrom(fieldClass)) {
return LocalDate.of(((ArrayNode) fieldNode).get(0).asInt(), ((ArrayNode) fieldNode).get(1).asInt(), ((ArrayNode) fieldNode).get(2).asInt());
} else { } else {
throw new NotImplementedException("property type not yet implemented: " + field); throw new NotImplementedException("property type not yet implemented: " + fieldNode + " -> " + fieldName + ": " + fieldClass);
} }
} }
@ -67,30 +99,36 @@ public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
ReflectionUtil.setValue(dto, field, ((Number) value).intValue()); ReflectionUtil.setValue(dto, field, ((Number) value).intValue());
} else if (Long.class.isAssignableFrom(field.getType()) || long.class.isAssignableFrom(field.getType())) { } else if (Long.class.isAssignableFrom(field.getType()) || long.class.isAssignableFrom(field.getType())) {
ReflectionUtil.setValue(dto, field, ((Number) value).longValue()); ReflectionUtil.setValue(dto, field, ((Number) value).longValue());
} else if (field.getType().isEnum()) {
ReflectionUtil.setValue(dto, field, Enum.valueOf((Class<Enum>) field.getType(), value.toString()));
} else if (LocalDate.class.isAssignableFrom(field.getType())) {
ReflectionUtil.setValue(dto, field, LocalDate.parse(value.toString()));
} else { } else {
throw new NotImplementedException("property type not yet implemented: " + field); throw new NotImplementedException("property type not yet implemented: " + field);
} }
writtenFields.add(field);
} }
private void markAsModified(final Field field) { private void checkAccessToWrittenFields(final T currentDto) {
modifiedFields.add(field); writtenFields.forEach(field -> {
} if (!field.equals(selfIdField)) {
final Role role = getLoginUserRole();
private void checkAccessToModifiedFields() {
modifiedFields.forEach(field -> {
if ( !field.equals(selfIdField) ) {
if (getId() == null) { if (getId() == null) {
if (!getLoginUserRole().isAllowedToInit(field)) { if (!role.isAllowedToInit(field)) {
if ( !field.equals(parentIdField)) { if (!field.equals(parentIdField)) {
throw new BadRequestAlertException("Initialization of field prohibited for current user", toDisplay(field), "initializationProhibited"); throw new BadRequestAlertException("Initialization of field " + toDisplay(field) + " prohibited for current user role " + role, toDisplay(field), "initializationProhibited");
} else { } else {
throw new BadRequestAlertException("Referencing field prohibited for current user", toDisplay(field), "referencingProhibited"); throw new BadRequestAlertException("Referencing field " + toDisplay(field) + " prohibited for current user role " + role, toDisplay(field), "referencingProhibited");
} }
} }
} else if (!getLoginUserRole().isAllowedToUpdate(field)) { } else if (isUpdate(field, dto, currentDto) && !getLoginUserRole().isAllowedToUpdate(field)){
throw new BadRequestAlertException("Update of field prohibited for current user", toDisplay(field), "updateProhibited"); throw new BadRequestAlertException("Update of field " + toDisplay(field) + " prohibited for current user role " + role, toDisplay(field), "updateProhibited");
} }
} }
}); });
} }
private boolean isUpdate(final Field field, final T dto, T currentDto) {
return ObjectUtils.notEqual(ReflectionUtil.getValue(dto, field), ReflectionUtil.getValue(currentDto, field));
}
} }

View File

@ -10,7 +10,7 @@ import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.time.LocalDate; import java.time.LocalDate;
public class JSonSerializerWithAccessFilter <T> extends JSonAccessFilter<T> { public class JSonSerializerWithAccessFilter<T> extends JSonAccessFilter<T> {
private final JsonGenerator jsonGenerator; private final JsonGenerator jsonGenerator;
private final SerializerProvider serializerProvider; private final SerializerProvider serializerProvider;
@ -27,32 +27,35 @@ public class JSonSerializerWithAccessFilter <T> extends JSonAccessFilter<T> {
public void serialize() throws IOException { public void serialize() throws IOException {
jsonGenerator.writeStartObject(); jsonGenerator.writeStartObject();
for (Field prop : dto.getClass().getDeclaredFields()) { for (Field field : dto.getClass().getDeclaredFields()) {
toJSon(dto, jsonGenerator, prop); toJSon(dto, jsonGenerator, field);
} }
jsonGenerator.writeEndObject(); jsonGenerator.writeEndObject();
} }
private void toJSon(final Object dto, final JsonGenerator jsonGenerator, final Field prop) throws IOException { private void toJSon(final Object dto, final JsonGenerator jsonGenerator, final Field field) throws IOException {
if (getLoginUserRole().isAllowedToRead(prop)) { if (getLoginUserRole().isAllowedToRead(field)) {
final String fieldName = prop.getName(); final String fieldName = field.getName();
// TODO: maybe replace by serializerProvider.defaultSerialize...()? // TODO: maybe replace by serializerProvider.defaultSerialize...()?
// But that makes it difficult for parallel structure with the deserializer (clumsy API). // But that makes it difficult for parallel structure with the deserializer (clumsy API).
// Alternatively extract the supported types to subclasses of some abstract class and // Alternatively extract the supported types to subclasses of some abstract class and
// here as well as in the deserializer just access the matching implementation through a map. // here as well as in the deserializer just access the matching implementation through a map.
// Or even completely switch from Jackson to GSON? // Or even completely switch from Jackson to GSON?
if (Integer.class.isAssignableFrom(prop.getType()) || int.class.isAssignableFrom(prop.getType())) { final Object fieldValue = get(dto, field);
jsonGenerator.writeNumberField(fieldName, (int) get(dto, prop)); if (fieldValue == null) {
} else if (Long.class.isAssignableFrom(prop.getType()) || long.class.isAssignableFrom(prop.getType())) { jsonGenerator.writeNullField(fieldName);
jsonGenerator.writeNumberField(fieldName, (long) get(dto, prop)); } else if (Integer.class.isAssignableFrom(field.getType()) || int.class.isAssignableFrom(field.getType())) {
} else if (LocalDate.class.isAssignableFrom(prop.getType())) { jsonGenerator.writeNumberField(fieldName, (int) fieldValue);
jsonGenerator.writeStringField(fieldName, get(dto, prop).toString()); // TODO proper format } else if (Long.class.isAssignableFrom(field.getType()) || long.class.isAssignableFrom(field.getType())) {
} else if (Enum.class.isAssignableFrom(prop.getType())) { jsonGenerator.writeNumberField(fieldName, (long) fieldValue);
jsonGenerator.writeStringField(fieldName, get(dto, prop).toString()); // TODO proper representation } else if (LocalDate.class.isAssignableFrom(field.getType())) {
} else if (String.class.isAssignableFrom(prop.getType())) { jsonGenerator.writeStringField(fieldName, fieldValue.toString()); // TODO proper format
jsonGenerator.writeStringField(fieldName, (String) get(dto, prop)); } else if (Enum.class.isAssignableFrom(field.getType())) {
jsonGenerator.writeStringField(fieldName, fieldValue.toString()); // TODO proper representation
} else if (String.class.isAssignableFrom(field.getType())) {
jsonGenerator.writeStringField(fieldName, (String) fieldValue);
} else { } else {
throw new NotImplementedException("property type not yet implemented: " + prop); throw new NotImplementedException("property type not yet implemented: " + field);
} }
} }
} }

View File

@ -33,17 +33,12 @@ public enum Role {
*/ */
SUPPORTER(3), SUPPORTER(3),
/**
* This meta-role is to specify that any kind of customer contact can get access to the resource.
*/
ANY_CUSTOMER_CONTACT(20),
/** /**
* This role is for contractual contacts of a customer, like a director of the company. * This role is for contractual contacts of a customer, like a director of the company.
* Who has this role, has the broadest access to all resources which belong to this customer. * Who has this role, has the broadest access to all resources which belong to this customer.
* Everything which relates to the contract with the customer, needs this role. * Everything which relates to the contract with the customer, needs this role.
*/ */
CONTRACTUAL_CONTACT(21), CONTRACTUAL_CONTACT(20),
/** /**
* This role is for financial contacts of a customer, e.g. for accessing billing data. * This role is for financial contacts of a customer, e.g. for accessing billing data.
@ -64,6 +59,11 @@ public enum Role {
TECHNICAL_CONTACT(22), TECHNICAL_CONTACT(22),
/**
* This meta-role is to specify that any kind of customer contact can get access to the resource.
*/
ANY_CUSTOMER_CONTACT(29),
/** /**
* Any user which belongs to a customer has at least this role. * Any user which belongs to a customer has at least this role.
*/ */

View File

@ -1,5 +1,7 @@
package org.hostsharing.hsadminng.service.accessfilter; package org.hostsharing.hsadminng.service.accessfilter;
import org.hostsharing.hsadminng.service.IdToDtoResolver;
import java.lang.annotation.*; import java.lang.annotation.*;
/** /**
@ -14,4 +16,6 @@ import java.lang.annotation.*;
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface SelfId { public @interface SelfId {
/// The service which can load the referenced DTO.
Class<? extends IdToDtoResolver<?>> resolver();
} }

View File

@ -34,7 +34,7 @@ public class AssetDTO implements Serializable {
private Long membershipId; private Long membershipId;
private String membershipAdmissionDocumentDate; private String membershipDisplayReference;
public Long getId() { public Long getId() {
return id; return id;
@ -92,12 +92,12 @@ public class AssetDTO implements Serializable {
this.membershipId = membershipId; this.membershipId = membershipId;
} }
public String getMembershipAdmissionDocumentDate() { public String getMembershipDisplayReference() {
return membershipAdmissionDocumentDate; return membershipDisplayReference;
} }
public void setMembershipAdmissionDocumentDate(String membershipAdmissionDocumentDate) { public void setMembershipDisplayReference(String membershipDisplayReference) {
this.membershipAdmissionDocumentDate = membershipAdmissionDocumentDate; this.membershipDisplayReference = membershipDisplayReference;
} }
@Override @Override
@ -131,7 +131,7 @@ public class AssetDTO implements Serializable {
", amount=" + getAmount() + ", amount=" + getAmount() +
", remark='" + getRemark() + "'" + ", remark='" + getRemark() + "'" +
", membership=" + getMembershipId() + ", membership=" + getMembershipId() +
", membership='" + getMembershipAdmissionDocumentDate() + "'" + ", membership='" + getMembershipDisplayReference() + "'" +
"}"; "}";
} }
} }

View File

@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.SerializerProvider;
import org.hostsharing.hsadminng.domain.enumeration.CustomerKind; import org.hostsharing.hsadminng.domain.enumeration.CustomerKind;
import org.hostsharing.hsadminng.domain.enumeration.VatRegion; import org.hostsharing.hsadminng.domain.enumeration.VatRegion;
import org.hostsharing.hsadminng.service.CustomerService;
import org.hostsharing.hsadminng.service.accessfilter.*; import org.hostsharing.hsadminng.service.accessfilter.*;
import org.springframework.boot.jackson.JsonComponent; import org.springframework.boot.jackson.JsonComponent;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -21,9 +22,9 @@ import java.util.Objects;
/** /**
* A DTO for the Customer entity. * A DTO for the Customer entity.
*/ */
public class CustomerDTO implements Serializable { public class CustomerDTO extends FluentBuilder<CustomerDTO> implements Serializable {
@SelfId @SelfId(resolver = CustomerService.class)
@AccessFor(read = Role.ANY_CUSTOMER_USER) @AccessFor(read = Role.ANY_CUSTOMER_USER)
private Long id; private Long id;
@ -41,31 +42,38 @@ public class CustomerDTO implements Serializable {
@NotNull @NotNull
@Size(max = 80) @Size(max = 80)
@AccessFor(init = Role.ADMIN, read = Role.ANY_CUSTOMER_USER) @AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.ANY_CUSTOMER_USER)
private String name; private String name;
@NotNull @NotNull
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.CONTRACTUAL_CONTACT)
private CustomerKind kind; private CustomerKind kind;
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private LocalDate birthDate; private LocalDate birthDate;
@Size(max = 80) @Size(max = 80)
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private String birthPlace; private String birthPlace;
@Size(max = 80) @Size(max = 80)
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private String registrationCourt; private String registrationCourt;
@Size(max = 80) @Size(max = 80)
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private String registrationNumber; private String registrationNumber;
@NotNull @NotNull
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private VatRegion vatRegion; private VatRegion vatRegion;
@Size(max = 40) @Size(max = 40)
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private String vatNumber; private String vatNumber;
@Size(max = 80) @Size(max = 80)
@AccessFor(init = Role.ADMIN, update = Role.CONTRACTUAL_CONTACT, read = Role.ANY_CUSTOMER_CONTACT) @AccessFor(init = Role.ADMIN, update = Role.CONTRACTUAL_CONTACT, read = Role.CONTRACTUAL_CONTACT)
private String contractualSalutation; private String contractualSalutation;
@NotNull @NotNull
@ -78,7 +86,7 @@ public class CustomerDTO implements Serializable {
private String billingSalutation; private String billingSalutation;
@Size(max = 400) @Size(max = 400)
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.CONTRACTUAL_CONTACT) @AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private String billingAddress; private String billingAddress;
@Size(max = 160) @Size(max = 160)

View File

@ -0,0 +1,15 @@
package org.hostsharing.hsadminng.service.dto;
import java.util.function.Consumer;
public class FluentBuilder<T> {
@SuppressWarnings("unchecked")
public T with(
Consumer<T> builderFunction) {
builderFunction.accept((T) this);
return (T) this;
}
}

View File

@ -1,6 +1,7 @@
package org.hostsharing.hsadminng.service.dto; package org.hostsharing.hsadminng.service.dto;
import org.hostsharing.hsadminng.service.CustomerService; import org.hostsharing.hsadminng.service.CustomerService;
import org.hostsharing.hsadminng.service.MembershipService;
import org.hostsharing.hsadminng.service.accessfilter.AccessFor; import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
import org.hostsharing.hsadminng.service.accessfilter.ParentId; import org.hostsharing.hsadminng.service.accessfilter.ParentId;
import org.hostsharing.hsadminng.service.accessfilter.Role; import org.hostsharing.hsadminng.service.accessfilter.Role;
@ -11,14 +12,13 @@ import javax.validation.constraints.Size;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
/** /**
* A DTO for the Membership entity. * A DTO for the Membership entity.
*/ */
public class MembershipDTO implements Serializable { public class MembershipDTO extends FluentBuilder<MembershipDTO> implements Serializable {
@SelfId @SelfId(resolver = MembershipService.class)
@AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT}) @AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private Long id; private Long id;
@ -47,12 +47,6 @@ public class MembershipDTO implements Serializable {
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT}) @AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private String customerPrefix; private String customerPrefix;
public MembershipDTO with(
Consumer<MembershipDTO> builderFunction) {
builderFunction.accept(this);
return this;
}
public Long getId() { public Long getId() {
return id; return id;
} }

View File

@ -1,5 +1,13 @@
package org.hostsharing.hsadminng.service.dto; package org.hostsharing.hsadminng.service.dto;
import org.hostsharing.hsadminng.domain.enumeration.ShareAction;
import org.hostsharing.hsadminng.service.MembershipService;
import org.hostsharing.hsadminng.service.ShareService;
import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
import org.hostsharing.hsadminng.service.accessfilter.ParentId;
import org.hostsharing.hsadminng.service.accessfilter.Role;
import org.hostsharing.hsadminng.service.accessfilter.SelfId;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.io.Serializable; import java.io.Serializable;
@ -11,18 +19,16 @@ import java.util.Objects;
*/ */
public class ShareDTO implements Serializable { public class ShareDTO implements Serializable {
@SelfId @SelfId(resolver = ShareService.class)
@AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT}) @AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private Long id; private Long id;
@NotNull @NotNull
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT}) @AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private LocalDate documentDate; private LocalDate documentDate;
@NotNull @NotNull
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT}) @AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private LocalDate valueDate; private LocalDate valueDate;
@NotNull @NotNull
@ -41,8 +47,8 @@ public class ShareDTO implements Serializable {
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT}) @AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private Long membershipId; private Long membershipId;
@AccessFor(init = Role.ADMIN, read = Role.SUPPORTER) @AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
private String membershipAdmissionDocumentDate; private String membershipDisplayReference;
public Long getId() { public Long getId() {
return id; return id;
@ -100,12 +106,12 @@ public class ShareDTO implements Serializable {
this.membershipId = membershipId; this.membershipId = membershipId;
} }
public String getMembershipAdmissionDocumentDate() { public String getMembershipDisplayReference() {
return membershipAdmissionDocumentDate; return membershipDisplayReference;
} }
public void setMembershipAdmissionDocumentDate(String membershipAdmissionDocumentDate) { public void setMembershipDisplayReference(String membershipDisplayReference) {
this.membershipAdmissionDocumentDate = membershipAdmissionDocumentDate; this.membershipDisplayReference = membershipDisplayReference;
} }
@Override @Override
@ -139,7 +145,7 @@ public class ShareDTO implements Serializable {
", quantity=" + getQuantity() + ", quantity=" + getQuantity() +
", remark='" + getRemark() + "'" + ", remark='" + getRemark() + "'" +
", membership=" + getMembershipId() + ", membership=" + getMembershipId() +
", membership='" + getMembershipAdmissionDocumentDate() + "'" + ", membership='" + getMembershipDisplayReference() + "'" +
"}"; "}";
} }
} }

View File

@ -12,7 +12,7 @@ import org.mapstruct.Mapping;
public interface AssetMapper extends EntityMapper<AssetDTO, Asset> { public interface AssetMapper extends EntityMapper<AssetDTO, Asset> {
@Mapping(source = "membership.id", target = "membershipId") @Mapping(source = "membership.id", target = "membershipId")
@Mapping(source = "membership.admissionDocumentDate", target = "membershipAdmissionDocumentDate") @Mapping(source = "membership.admissionDocumentDate", target = "membershipDisplayReference")
AssetDTO toDto(Asset asset); AssetDTO toDto(Asset asset);
@Mapping(source = "membershipId", target = "membership") @Mapping(source = "membershipId", target = "membership")

View File

@ -12,7 +12,7 @@ import org.mapstruct.Mapping;
public interface ShareMapper extends EntityMapper<ShareDTO, Share> { public interface ShareMapper extends EntityMapper<ShareDTO, Share> {
@Mapping(source = "membership.id", target = "membershipId") @Mapping(source = "membership.id", target = "membershipId")
@Mapping(source = "membership.admissionDocumentDate", target = "membershipAdmissionDocumentDate") @Mapping(source = "membership.admissionDocumentDate", target = "membershipDisplayReference")
ShareDTO toDto(Share share); ShareDTO toDto(Share share);
@Mapping(source = "membershipId", target = "membership") @Mapping(source = "membershipId", target = "membership")

View File

@ -1,25 +1,24 @@
package org.hostsharing.hsadminng.web.rest; package org.hostsharing.hsadminng.web.rest;
import io.github.jhipster.web.util.ResponseUtil;
import org.hostsharing.hsadminng.service.CustomerQueryService;
import org.hostsharing.hsadminng.service.CustomerService; import org.hostsharing.hsadminng.service.CustomerService;
import org.hostsharing.hsadminng.service.dto.CustomerCriteria;
import org.hostsharing.hsadminng.service.dto.CustomerDTO;
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
import org.hostsharing.hsadminng.web.rest.util.HeaderUtil; import org.hostsharing.hsadminng.web.rest.util.HeaderUtil;
import org.hostsharing.hsadminng.web.rest.util.PaginationUtil; import org.hostsharing.hsadminng.web.rest.util.PaginationUtil;
import org.hostsharing.hsadminng.service.dto.CustomerDTO;
import org.hostsharing.hsadminng.service.dto.CustomerCriteria;
import org.hostsharing.hsadminng.service.CustomerQueryService;
import io.github.jhipster.web.util.ResponseUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.validation.Valid; import javax.validation.Valid;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -72,7 +71,7 @@ public class CustomerResource {
* @throws URISyntaxException if the Location URI syntax is incorrect * @throws URISyntaxException if the Location URI syntax is incorrect
*/ */
@PutMapping("/customers") @PutMapping("/customers")
public ResponseEntity<CustomerDTO> updateCustomer(@Valid @RequestBody CustomerDTO customerDTO) throws URISyntaxException { public ResponseEntity<CustomerDTO> updateCustomer(@RequestBody CustomerDTO customerDTO) {
log.debug("REST request to update Customer : {}", customerDTO); log.debug("REST request to update Customer : {}", customerDTO);
if (customerDTO.getId() == null) { if (customerDTO.getId() == null) {
throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");

View File

@ -28,7 +28,7 @@
<dt><span jhiTranslate="hsadminNgApp.asset.membership">Membership</span></dt> <dt><span jhiTranslate="hsadminNgApp.asset.membership">Membership</span></dt>
<dd> <dd>
<div *ngIf="asset.membershipId"> <div *ngIf="asset.membershipId">
<a [routerLink]="['/membership', asset.membershipId, 'view']">{{asset.membershipAdmissionDocumentDate}}</a> <a [routerLink]="['/membership', asset.membershipId, 'view']">{{asset.membershipDisplayReference}}</a>
</div> </div>
</dd> </dd>
</dl> </dl>

View File

@ -20,7 +20,7 @@
<th jhiSortBy="action"><span jhiTranslate="hsadminNgApp.asset.action">Action</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="action"><span jhiTranslate="hsadminNgApp.asset.action">Action</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="amount"><span jhiTranslate="hsadminNgApp.asset.amount">Amount</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="amount"><span jhiTranslate="hsadminNgApp.asset.amount">Amount</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="remark"><span jhiTranslate="hsadminNgApp.asset.remark">Remark</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="remark"><span jhiTranslate="hsadminNgApp.asset.remark">Remark</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="membershipAdmissionDocumentDate"><span jhiTranslate="hsadminNgApp.asset.membership">Membership</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="membershipDisplayReference"><span jhiTranslate="hsadminNgApp.asset.membership">Membership</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -34,7 +34,7 @@
<td>{{asset.remark}}</td> <td>{{asset.remark}}</td>
<td> <td>
<div *ngIf="asset.membershipId"> <div *ngIf="asset.membershipId">
<a [routerLink]="['../membership', asset.membershipId , 'view' ]" >{{asset.membershipAdmissionDocumentDate}}</a> <a [routerLink]="['../membership', asset.membershipId , 'view' ]" >{{asset.membershipDisplayReference}}</a>
</div> </div>
</td> </td>
<td class="text-right"> <td class="text-right">

View File

@ -28,7 +28,7 @@
<dt><span jhiTranslate="hsadminNgApp.share.membership">Membership</span></dt> <dt><span jhiTranslate="hsadminNgApp.share.membership">Membership</span></dt>
<dd> <dd>
<div *ngIf="share.membershipId"> <div *ngIf="share.membershipId">
<a [routerLink]="['/membership', share.membershipId, 'view']">{{share.membershipAdmissionDocumentDate}}</a> <a [routerLink]="['/membership', share.membershipId, 'view']">{{share.membershipDisplayReference}}</a>
</div> </div>
</dd> </dd>
</dl> </dl>

View File

@ -20,7 +20,7 @@
<th jhiSortBy="action"><span jhiTranslate="hsadminNgApp.share.action">Action</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="action"><span jhiTranslate="hsadminNgApp.share.action">Action</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="quantity"><span jhiTranslate="hsadminNgApp.share.quantity">Quantity</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="quantity"><span jhiTranslate="hsadminNgApp.share.quantity">Quantity</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="remark"><span jhiTranslate="hsadminNgApp.share.remark">Remark</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="remark"><span jhiTranslate="hsadminNgApp.share.remark">Remark</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="membershipAdmissionDocumentDate"><span jhiTranslate="hsadminNgApp.share.membership">Membership</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="membershipDisplayReference"><span jhiTranslate="hsadminNgApp.share.membership">Membership</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -34,7 +34,7 @@
<td>{{share.remark}}</td> <td>{{share.remark}}</td>
<td> <td>
<div *ngIf="share.membershipId"> <div *ngIf="share.membershipId">
<a [routerLink]="['../membership', share.membershipId , 'view' ]" >{{share.membershipAdmissionDocumentDate}}</a> <a [routerLink]="['../membership', share.membershipId , 'view' ]" >{{share.membershipDisplayReference}}</a>
</div> </div>
</td> </td>
<td class="text-right"> <td class="text-right">

View File

@ -16,7 +16,7 @@ export interface IAsset {
action?: AssetAction; action?: AssetAction;
amount?: number; amount?: number;
remark?: string; remark?: string;
membershipAdmissionDocumentDate?: string; membershipDisplayReference?: string;
membershipId?: number; membershipId?: number;
} }
@ -28,7 +28,7 @@ export class Asset implements IAsset {
public action?: AssetAction, public action?: AssetAction,
public amount?: number, public amount?: number,
public remark?: string, public remark?: string,
public membershipAdmissionDocumentDate?: string, public membershipDisplayReference?: string,
public membershipId?: number public membershipId?: number
) {} ) {}
} }

View File

@ -12,7 +12,7 @@ export interface IShare {
action?: ShareAction; action?: ShareAction;
quantity?: number; quantity?: number;
remark?: string; remark?: string;
membershipAdmissionDocumentDate?: string; membershipDisplayReference?: string;
membershipId?: number; membershipId?: number;
} }
@ -24,7 +24,7 @@ export class Share implements IShare {
public action?: ShareAction, public action?: ShareAction,
public quantity?: number, public quantity?: number,
public remark?: string, public remark?: string,
public membershipAdmissionDocumentDate?: string, public membershipDisplayReference?: string,
public membershipId?: number public membershipId?: number
) {} ) {}
} }

View File

@ -1,5 +1,6 @@
{ {
"error": { "error": {
"idNotFound": "Technische Datensatz-ID nicht gefunden",
"shareSubscriptionPositiveQuantity": "Zeichnungen von Geschäftsanteilen erfordern eine positive Stückzahl", "shareSubscriptionPositiveQuantity": "Zeichnungen von Geschäftsanteilen erfordern eine positive Stückzahl",
"shareCancellationNegativeQuantity": "Kündigungen von Geschäftsanteilen erfordern eine negative Stückzahl", "shareCancellationNegativeQuantity": "Kündigungen von Geschäftsanteilen erfordern eine negative Stückzahl",
"shareTransactionImmutable": "Transaktionen mit Geschäftsanteilen sind unveränderlich", "shareTransactionImmutable": "Transaktionen mit Geschäftsanteilen sind unveränderlich",

View File

@ -1,5 +1,6 @@
{ {
"error": { "error": {
"idNotFound": "Technical record-ID not found",
"shareSubscriptionPositiveQuantity": "Share subscriptions require a positive quantity", "shareSubscriptionPositiveQuantity": "Share subscriptions require a positive quantity",
"shareCancellationNegativeQuantity": "Share cancellations require a negative quantity", "shareCancellationNegativeQuantity": "Share cancellations require a negative quantity",
"shareTransactionImmutable": "Share transactions are immutable", "shareTransactionImmutable": "Share transactions are immutable",

View File

@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.ImmutablePair;
import org.hostsharing.hsadminng.service.IdToDtoResolver; import org.hostsharing.hsadminng.service.IdToDtoResolver;
import org.hostsharing.hsadminng.service.dto.FluentBuilder;
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
@ -13,9 +14,11 @@ import org.junit.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable; import static org.assertj.core.api.Assertions.catchThrowable;
@ -31,22 +34,41 @@ public class JSonDeserializerWithAccessFilterUnitTest {
public MockitoRule mockitoRule = MockitoJUnit.rule(); public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock @Mock
public ApplicationContext ctx; private ApplicationContext ctx;
@Mock @Mock
public JsonParser jsonParser; private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Mock @Mock
public ObjectCodec codec; private JsonParser jsonParser;
@Mock @Mock
public TreeNode treeNode; private ObjectCodec codec;
@Mock
private TreeNode treeNode;
@Mock
private GivenService givenService;
@Mock
private GivenChildService givenChildService;
@Before @Before
public void init() { public void init() {
givenAuthenticatedUser(); givenAuthenticatedUser();
givenUserHavingRole(GivenDto.class, 1234L, Role.ACTUAL_CUSTOMER_USER); givenUserHavingRole(GivenDto.class, 1234L, Role.ACTUAL_CUSTOMER_USER);
given (ctx.getAutowireCapableBeanFactory()).willReturn(autowireCapableBeanFactory);
given(autowireCapableBeanFactory.createBean(GivenService.class)).willReturn(givenService);
given(givenService.findOne(1234L)).willReturn(Optional.of(new GivenDto()
.with(dto -> dto.id = 1234L)
.with(dto -> dto.openIntegerField = 1)
.with(dto -> dto.openLongField = 2L)
.with(dto -> dto.openStringField = "3")
.with(dto -> dto.restrictedField = "initial value of restricted field")
));
given(jsonParser.getCodec()).willReturn(codec); given(jsonParser.getCodec()).willReturn(codec);
} }
@ -99,21 +121,54 @@ public class JSonDeserializerWithAccessFilterUnitTest {
givenUserHavingRole(GivenDto.class, 1234L, Role.FINANCIAL_CONTACT); givenUserHavingRole(GivenDto.class, 1234L, Role.FINANCIAL_CONTACT);
givenJSonTree(asJSon( givenJSonTree(asJSon(
ImmutablePair.of("id", 1234L), ImmutablePair.of("id", 1234L),
ImmutablePair.of("restrictedField", "Restricted String Value"))); ImmutablePair.of("restrictedField", "update value of restricted field")));
// when // when
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize(); GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize();
// then // then
assertThat(actualDto.restrictedField).isEqualTo("Restricted String Value"); assertThat(actualDto.restrictedField).isEqualTo("update value of restricted field");
}
@Test
public void shouldDeserializeUnchangedStringFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
// given
givenAuthenticatedUser();
givenUserHavingRole(GivenDto.class, 1234L, Role.ANY_CUSTOMER_USER);
givenJSonTree(asJSon(
ImmutablePair.of("id", 1234L),
ImmutablePair.of("restrictedField", "initial value of restricted field")));
// when
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize();
// then
assertThat(actualDto.restrictedField).isEqualTo("initial value of restricted field");
}
@Test
public void shouldNotDeserializeUpatedStringFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
// given
givenAuthenticatedUser();
givenUserHavingRole(GivenDto.class, 1L, Role.ANY_CUSTOMER_USER);
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "updated value of restricted field")));
// when
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize());
// then
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
assertThat(badRequestAlertException.getParam()).isEqualTo("GivenDto.restrictedField");
assertThat(badRequestAlertException.getErrorKey()).isEqualTo("initializationProhibited");
});
} }
@Test @Test
public void shouldInitializeFieldIfRequiredRoleIsNotCoveredByUser() throws IOException { public void shouldInitializeFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
// given // given
givenAuthenticatedUser(); givenAuthenticatedUser();
givenUserHavingRole(null, null, Role.ANY_CUSTOMER_USER); givenUserHavingRole(GivenDto.class, 1L, Role.ANY_CUSTOMER_USER);
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "Restricted String Value"))); givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "another value of restricted field")));
// when // when
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize()); Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize());
@ -130,7 +185,7 @@ public class JSonDeserializerWithAccessFilterUnitTest {
// given // given
givenAuthenticatedUser(); givenAuthenticatedUser();
givenUserHavingRole(GivenDto.class, 9999L, Role.CONTRACTUAL_CONTACT); givenUserHavingRole(GivenDto.class, 9999L, Role.CONTRACTUAL_CONTACT);
givenJSonTree(asJSon(ImmutablePair.of("parentId", 1111L))); givenJSonTree(asJSon(ImmutablePair.of("parentId", 1234L)));
// when // when
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize()); Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize());
@ -146,14 +201,14 @@ public class JSonDeserializerWithAccessFilterUnitTest {
public void shouldCreateIfRoleRequiredByReferencedEntityIsCoveredByUser() throws IOException { public void shouldCreateIfRoleRequiredByReferencedEntityIsCoveredByUser() throws IOException {
// given // given
givenAuthenticatedUser(); givenAuthenticatedUser();
givenUserHavingRole(GivenDto.class, 1111L, Role.CONTRACTUAL_CONTACT); givenUserHavingRole(GivenDto.class, 1234L, Role.CONTRACTUAL_CONTACT);
givenJSonTree(asJSon(ImmutablePair.of("parentId", 1111L))); givenJSonTree(asJSon(ImmutablePair.of("parentId", 1234L)));
// when // when
final GivenChildDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize(); final GivenChildDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize();
// then // then
assertThat(actualDto.parentId).isEqualTo(1111L); assertThat(actualDto.parentId).isEqualTo(1234L);
} }
@Test @Test
@ -193,9 +248,12 @@ public class JSonDeserializerWithAccessFilterUnitTest {
given(codec.readTree(jsonParser)).willReturn(new ObjectMapper().readTree(givenJSon)); given(codec.readTree(jsonParser)).willReturn(new ObjectMapper().readTree(givenJSon));
} }
public static class GivenDto { abstract class GivenService implements IdToDtoResolver<GivenDto> {
}
@SelfId public static class GivenDto extends FluentBuilder<GivenDto> {
@SelfId(resolver = GivenService.class)
@AccessFor(read = Role.ANY_CUSTOMER_USER) @AccessFor(read = Role.ANY_CUSTOMER_USER)
Long id; Long id;
@ -212,12 +270,12 @@ public class JSonDeserializerWithAccessFilterUnitTest {
Long openLongField; Long openLongField;
} }
abstract class GivenService implements IdToDtoResolver<GivenDto> { abstract class GivenChildService implements IdToDtoResolver<GivenChildDto> {
} }
public static class GivenChildDto { public static class GivenChildDto extends FluentBuilder<GivenChildDto> {
@SelfId @SelfId(resolver = GivenChildService.class)
@AccessFor(read = Role.ANY_CUSTOMER_USER) @AccessFor(read = Role.ANY_CUSTOMER_USER)
Long id; Long id;
@ -231,11 +289,11 @@ public class JSonDeserializerWithAccessFilterUnitTest {
public static class GivenDtoWithMultipleSelfId { public static class GivenDtoWithMultipleSelfId {
@SelfId @SelfId(resolver = GivenChildService.class)
@AccessFor(read = Role.ANY_CUSTOMER_USER) @AccessFor(read = Role.ANY_CUSTOMER_USER)
Long id; Long id;
@SelfId @SelfId(resolver = GivenChildService.class)
@AccessFor(read = Role.ANY_CUSTOMER_USER) @AccessFor(read = Role.ANY_CUSTOMER_USER)
Long id2; Long id2;

View File

@ -5,18 +5,22 @@ import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.RandomUtils;
import org.hostsharing.hsadminng.service.IdToDtoResolver; import org.hostsharing.hsadminng.service.IdToDtoResolver;
import org.hostsharing.hsadminng.service.dto.FluentBuilder;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable; import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -26,10 +30,16 @@ public class JSonSerializerWithAccessFilterUnitTest {
public MockitoRule mockitoRule = MockitoJUnit.rule(); public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock @Mock
public ApplicationContext ctx; private ApplicationContext ctx;
@Mock @Mock
public JsonGenerator jsonGenerator; private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Mock
private JsonGenerator jsonGenerator;
@Mock
private GivenCustomerService givenCustomerService;
private final GivenDto givenDTO = createSampleDto(); private final GivenDto givenDTO = createSampleDto();
@ -37,6 +47,12 @@ public class JSonSerializerWithAccessFilterUnitTest {
public void init() { public void init() {
MockSecurityContext.givenAuthenticatedUser(); MockSecurityContext.givenAuthenticatedUser();
MockSecurityContext.givenUserHavingRole(GivenCustomerDto.class, 888L, Role.ANY_CUSTOMER_USER); MockSecurityContext.givenUserHavingRole(GivenCustomerDto.class, 888L, Role.ANY_CUSTOMER_USER);
given(ctx.getAutowireCapableBeanFactory()).willReturn(autowireCapableBeanFactory);
given(autowireCapableBeanFactory.createBean(GivenCustomerService.class)).willReturn(givenCustomerService);
given(givenCustomerService.findOne(888L)).willReturn(Optional.of(new GivenCustomerDto()
.with(dto -> dto.id = 888L)
));
} }
@Test @Test
@ -84,7 +100,7 @@ public class JSonSerializerWithAccessFilterUnitTest {
} }
class GivenDtoWithUnimplementedFieldType { class GivenDtoWithUnimplementedFieldType {
@AccessFor(read = Role.ANYBODY) @AccessFor(read = Role.ANYBODY)
Arbitrary fieldWithUnimplementedType; Arbitrary fieldWithUnimplementedType = new Arbitrary();
} }
final GivenDtoWithUnimplementedFieldType givenDtoWithUnimplementedFieldType = new GivenDtoWithUnimplementedFieldType(); final GivenDtoWithUnimplementedFieldType givenDtoWithUnimplementedFieldType = new GivenDtoWithUnimplementedFieldType();
@ -107,8 +123,10 @@ public class JSonSerializerWithAccessFilterUnitTest {
return dto; return dto;
} }
private static class GivenCustomerDto { private static class GivenCustomerDto extends FluentBuilder<GivenCustomerDto> {
@SelfId(resolver = GivenService.class)
@AccessFor(read = Role.ANYBODY)
Long id;
} }
private abstract class GivenCustomerService implements IdToDtoResolver<GivenCustomerDto> { private abstract class GivenCustomerService implements IdToDtoResolver<GivenCustomerDto> {
@ -116,7 +134,12 @@ public class JSonSerializerWithAccessFilterUnitTest {
private static class GivenDto { private static class GivenDto {
@SelfId(resolver = GivenService.class)
@AccessFor(read = Role.ANYBODY)
Long id;
@ParentId(resolver = GivenCustomerService.class) @ParentId(resolver = GivenCustomerService.class)
@AccessFor(read = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
Long customerId; Long customerId;
@AccessFor(read = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT}) @AccessFor(read = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
@ -131,4 +154,9 @@ public class JSonSerializerWithAccessFilterUnitTest {
@AccessFor(read = Role.ANYBODY) @AccessFor(read = Role.ANYBODY)
Long openLongField; Long openLongField;
} }
private abstract class GivenService implements IdToDtoResolver<GivenService> {
}
} }

View File

@ -20,8 +20,13 @@ public class MockSecurityContext {
public static void givenUserHavingRole(final Class<?> onClass, final Long onId, final Role role) { public static void givenUserHavingRole(final Class<?> onClass, final Long onId, final Role role) {
if ((onClass == null || onId == null) && !role.isIndependent()) { if ((onClass == null || onId == null) && !role.isIndependent()) {
throw new IllegalArgumentException("dependent roles like " + role + " depend on DtoClass and ID"); throw new IllegalArgumentException("dependent roles like " + role + " missing DtoClass and ID");
} }
SecurityUtils.addUserRole(onClass, onId, role); SecurityUtils.addUserRole(onClass, onId, role);
} }
public static void givenUserHavingRole(final Role role) {
givenUserHavingRole(null, null, role);
}
} }

View File

@ -28,7 +28,7 @@ public class RoleUnitTest {
assertThat(Role.SUPPORTER.covers(Role.ADMIN)).isFalse(); assertThat(Role.SUPPORTER.covers(Role.ADMIN)).isFalse();
assertThat(Role.ANY_CUSTOMER_CONTACT.covers(Role.SUPPORTER)).isFalse(); assertThat(Role.ANY_CUSTOMER_CONTACT.covers(Role.SUPPORTER)).isFalse();
assertThat(Role.CONTRACTUAL_CONTACT.covers(Role.ANY_CUSTOMER_CONTACT)).isFalse(); assertThat(Role.ANY_CUSTOMER_CONTACT.covers(Role.CONTRACTUAL_CONTACT)).isFalse();
assertThat(Role.FINANCIAL_CONTACT.covers(Role.CONTRACTUAL_CONTACT)).isFalse(); assertThat(Role.FINANCIAL_CONTACT.covers(Role.CONTRACTUAL_CONTACT)).isFalse();
assertThat(Role.FINANCIAL_CONTACT.covers(Role.TECHNICAL_CONTACT)).isFalse(); assertThat(Role.FINANCIAL_CONTACT.covers(Role.TECHNICAL_CONTACT)).isFalse();
assertThat(Role.TECHNICAL_CONTACT.covers(Role.CONTRACTUAL_CONTACT)).isFalse(); assertThat(Role.TECHNICAL_CONTACT.covers(Role.CONTRACTUAL_CONTACT)).isFalse();
@ -55,7 +55,7 @@ public class RoleUnitTest {
assertThat(Role.SUPPORTER.covers(Role.ANY_CUSTOMER_CONTACT)).isTrue(); assertThat(Role.SUPPORTER.covers(Role.ANY_CUSTOMER_CONTACT)).isTrue();
assertThat(Role.ANY_CUSTOMER_CONTACT.covers(Role.CONTRACTUAL_CONTACT)).isTrue(); assertThat(Role.CONTRACTUAL_CONTACT.covers(Role.ANY_CUSTOMER_CONTACT)).isTrue();
assertThat(Role.CONTRACTUAL_CONTACT.covers(Role.FINANCIAL_CONTACT)).isTrue(); assertThat(Role.CONTRACTUAL_CONTACT.covers(Role.FINANCIAL_CONTACT)).isTrue();
assertThat(Role.CONTRACTUAL_CONTACT.covers(Role.TECHNICAL_CONTACT)).isTrue(); assertThat(Role.CONTRACTUAL_CONTACT.covers(Role.TECHNICAL_CONTACT)).isTrue();
assertThat(Role.TECHNICAL_CONTACT.covers(Role.ANY_CUSTOMER_USER)).isTrue(); assertThat(Role.TECHNICAL_CONTACT.covers(Role.ANY_CUSTOMER_USER)).isTrue();

View File

@ -2,54 +2,98 @@ package org.hostsharing.hsadminng.service.dto;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.hostsharing.hsadminng.domain.Customer;
import org.hostsharing.hsadminng.domain.enumeration.CustomerKind;
import org.hostsharing.hsadminng.domain.enumeration.VatRegion;
import org.hostsharing.hsadminng.repository.CustomerRepository;
import org.hostsharing.hsadminng.service.CustomerService;
import org.hostsharing.hsadminng.service.accessfilter.Role; import org.hostsharing.hsadminng.service.accessfilter.Role;
import org.hostsharing.hsadminng.service.mapper.CustomerMapper;
import org.hostsharing.hsadminng.service.mapper.CustomerMapperImpl;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest; import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenAuthenticatedUser; import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenAuthenticatedUser;
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenUserHavingRole; import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenUserHavingRole;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
@JsonTest @JsonTest
@SpringBootTest(classes = {CustomerMapperImpl.class, CustomerRepository.class, CustomerService.class, CustomerDTO.CustomerJsonSerializer.class, CustomerDTO.CustomerJsonDeserializer.class})
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
public class CustomerDTOUnitTest { public class CustomerDTOUnitTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired
private CustomerMapper customerMapper;
@MockBean
private CustomerRepository customerRepository;
@MockBean
private CustomerService customerService;
@Test @Test
public void testSerializationAsContractualCustomerContact() throws JsonProcessingException { public void testSerializationAsContractualCustomerContact() throws JsonProcessingException {
// given // given
givenAuthenticatedUser(); givenAuthenticatedUser();
givenUserHavingRole(CustomerDTO.class, null, Role.ANY_CUSTOMER_USER); givenUserHavingRole(CustomerDTO.class, 1234L, Role.CONTRACTUAL_CONTACT);
CustomerDTO given = createSomeCustomerDTO(); CustomerDTO given = createSomeCustomerDTO(1234L);
// when // when
String actual = objectMapper.writeValueAsString(given); String actual = objectMapper.writeValueAsString(given);
// then // then
given.setContractualAddress(null);
given.setContractualSalutation(null);
given.setBillingAddress(null);
given.setBillingSalutation(null);
given.setRemark(null); given.setRemark(null);
assertEquals(createExpectedJSon(given), actual); assertEquals(createExpectedJSon(given), actual);
} }
@Test
public void testSerializationAsTechnicalCustomerUser() throws JsonProcessingException {
// given
givenAuthenticatedUser();
givenUserHavingRole(CustomerDTO.class, 1234L, Role.TECHNICAL_CONTACT);
CustomerDTO given = createSomeCustomerDTO(1234L);
// when
String actual = objectMapper.writeValueAsString(given);
// then
final String expectedJSon = "{" +
toJSonFieldDefinition("id", given.getId()) + "," +
toJSonFieldDefinition("reference", given.getReference()) + "," +
toJSonFieldDefinition("prefix", given.getPrefix()) + "," +
toJSonFieldDefinition("name", given.getName()) +
"}";
assertEquals(expectedJSon, actual);
}
@Test @Test
public void testSerializationAsSupporter() throws JsonProcessingException { public void testSerializationAsSupporter() throws JsonProcessingException {
// given // given
givenAuthenticatedUser(); givenAuthenticatedUser();
givenUserHavingRole(CustomerDTO.class, null, Role.SUPPORTER); givenUserHavingRole(CustomerDTO.class, null, Role.SUPPORTER);
CustomerDTO given = createSomeCustomerDTO(); CustomerDTO given = createSomeCustomerDTO(1234L);
// when // when
String actual = objectMapper.writeValueAsString(given); String actual = objectMapper.writeValueAsString(given);
@ -62,7 +106,8 @@ public class CustomerDTOUnitTest {
public void testDeserializeAsContractualCustomerContact() throws IOException { public void testDeserializeAsContractualCustomerContact() throws IOException {
// given // given
givenAuthenticatedUser(); givenAuthenticatedUser();
givenUserHavingRole(CustomerDTO.class, null, Role.CONTRACTUAL_CONTACT); givenUserHavingRole(CustomerDTO.class, 1234L, Role.CONTRACTUAL_CONTACT);
given(customerRepository.findById(1234L)).willReturn(Optional.of(new Customer().id(1234L)));
String json = "{\"id\":1234,\"contractualSalutation\":\"Hallo Updated\",\"billingSalutation\":\"Moin Updated\"}"; String json = "{\"id\":1234,\"contractualSalutation\":\"Hallo Updated\",\"billingSalutation\":\"Moin Updated\"}";
// when // when
@ -84,14 +129,35 @@ public class CustomerDTOUnitTest {
toJSonFieldDefinitionIfPresent("reference", dto.getReference()) + toJSonFieldDefinitionIfPresent("reference", dto.getReference()) +
toJSonFieldDefinitionIfPresent("prefix", dto.getPrefix()) + toJSonFieldDefinitionIfPresent("prefix", dto.getPrefix()) +
toJSonFieldDefinitionIfPresent("name", dto.getName()) + toJSonFieldDefinitionIfPresent("name", dto.getName()) +
toJSonFieldDefinitionIfPresent("kind", "LEGAL") +
toJSonNullFieldDefinition("birthDate") +
toJSonNullFieldDefinition("birthPlace") +
toJSonFieldDefinitionIfPresent("registrationCourt", "Registergericht") +
toJSonFieldDefinitionIfPresent("registrationNumber", "Registernummer") +
toJSonFieldDefinitionIfPresent("vatRegion", "DOMESTIC") +
toJSonFieldDefinitionIfPresent("vatNumber", "DE1234") +
toJSonFieldDefinitionIfPresent("contractualSalutation", dto.getContractualSalutation()) + toJSonFieldDefinitionIfPresent("contractualSalutation", dto.getContractualSalutation()) +
toJSonFieldDefinitionIfPresent("contractualAddress", dto.getContractualAddress()) + toJSonFieldDefinitionIfPresent("contractualAddress", dto.getContractualAddress()) +
toJSonFieldDefinitionIfPresent("billingSalutation", dto.getBillingSalutation()) + toJSonFieldDefinitionIfPresent("billingSalutation", dto.getBillingSalutation()) +
toJSonFieldDefinitionIfPresent("billingAddress", dto.getBillingAddress()) + toJSonFieldDefinitionIfPresent("billingAddress", dto.getBillingAddress()) +
toJSonFieldDefinitionIfPresent("remark", dto.getRemark()) ; toJSonFieldDefinitionIfPresent("remark", dto.getRemark());
return "{" + json.substring(0, json.length() - 1) + "}"; return "{" + json.substring(0, json.length() - 1) + "}";
} }
private String toJSonFieldDefinition(String name, String value) {
return inQuotes(name) + ":" + (value != null ? inQuotes(value) : "null");
}
private String toJSonFieldDefinition(String name, Number value) {
return inQuotes(name) + ":" + (value != null ? value : "null");
}
private String toJSonNullFieldDefinition(String name) {
return inQuotes(name) + ":null,";
}
private String toJSonFieldDefinitionIfPresent(String name, String value) { private String toJSonFieldDefinitionIfPresent(String name, String value) {
return value != null ? inQuotes(name) + ":" + inQuotes(value) + "," : ""; return value != null ? inQuotes(name) + ":" + inQuotes(value) + "," : "";
} }
@ -104,12 +170,17 @@ public class CustomerDTOUnitTest {
return "\"" + value.toString() + "\""; return "\"" + value.toString() + "\"";
} }
private CustomerDTO createSomeCustomerDTO() { private CustomerDTO createSomeCustomerDTO(final long id) {
CustomerDTO given = new CustomerDTO(); final CustomerDTO given = new CustomerDTO();
given.setId(1234L); given.setId(id);
given.setReference(10001); given.setReference(10001);
given.setPrefix("abc"); given.setPrefix("abc");
given.setName("Mein Name"); given.setName("Mein Name");
given.setKind(CustomerKind.LEGAL);
given.setRegistrationCourt("Registergericht");
given.setRegistrationNumber("Registernummer");
given.setVatRegion(VatRegion.DOMESTIC);
given.setVatNumber("DE1234");
given.setContractualAddress("Eine Adresse"); given.setContractualAddress("Eine Adresse");
given.setContractualSalutation("Hallo"); given.setContractualSalutation("Hallo");
given.setBillingAddress("Noch eine Adresse"); given.setBillingAddress("Noch eine Adresse");

View File

@ -5,6 +5,8 @@ import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.tuple.ImmutablePair; 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.JSonDeserializerWithAccessFilter;
import org.hostsharing.hsadminng.service.accessfilter.Role; import org.hostsharing.hsadminng.service.accessfilter.Role;
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException; import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
@ -14,9 +16,11 @@ import org.junit.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable; import static org.assertj.core.api.Assertions.catchThrowable;
@ -31,20 +35,36 @@ public class MembershipDTOUnitTest {
public MockitoRule mockitoRule = MockitoJUnit.rule(); public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock @Mock
public ApplicationContext ctx; private ApplicationContext ctx;
@Mock @Mock
public JsonParser jsonParser; private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Mock @Mock
public ObjectCodec codec; private JsonParser jsonParser;
@Mock @Mock
public TreeNode treeNode; private ObjectCodec codec;
@Mock
private TreeNode treeNode;
@Mock
private MembershipService membershipService;
@Mock
private CustomerService customerService;
@Before @Before
public void init() { public void init() {
given(jsonParser.getCodec()).willReturn(codec); given(jsonParser.getCodec()).willReturn(codec);
given (ctx.getAutowireCapableBeanFactory()).willReturn(autowireCapableBeanFactory);
given(autowireCapableBeanFactory.createBean(MembershipService.class)).willReturn(membershipService);
given(autowireCapableBeanFactory.createBean(CustomerService.class)).willReturn(customerService);
given(customerService.findOne(1234L)).willReturn(Optional.of(new CustomerDTO()
.with(dto -> dto.setId(1234L))
));
} }
@Test @Test

View File

@ -154,7 +154,7 @@ public class ShareDTOUnitTest {
givenDTO.setAction(ShareAction.SUBSCRIPTION); givenDTO.setAction(ShareAction.SUBSCRIPTION);
givenDTO.setQuantity(3); givenDTO.setQuantity(3);
givenDTO.setDocumentDate(LocalDate.parse("2019-04-22")); givenDTO.setDocumentDate(LocalDate.parse("2019-04-22"));
givenDTO.setMembershipDocumentDate("2019-04-21"); // TODO: why is this not a LocalDate? givenDTO.setMembershipDisplayReference("2019-04-21"); // TODO: why is this not a LocalDate?
givenDTO.setValueDate(LocalDate.parse("2019-04-30")); givenDTO.setValueDate(LocalDate.parse("2019-04-30"));
givenDTO.setRemark("Some Remark"); givenDTO.setRemark("Some Remark");
return givenDTO; return givenDTO;

View File

@ -9,6 +9,7 @@ import org.hostsharing.hsadminng.domain.enumeration.VatRegion;
import org.hostsharing.hsadminng.repository.CustomerRepository; import org.hostsharing.hsadminng.repository.CustomerRepository;
import org.hostsharing.hsadminng.service.CustomerQueryService; import org.hostsharing.hsadminng.service.CustomerQueryService;
import org.hostsharing.hsadminng.service.CustomerService; import org.hostsharing.hsadminng.service.CustomerService;
import org.hostsharing.hsadminng.service.accessfilter.Role;
import org.hostsharing.hsadminng.service.dto.CustomerDTO; import org.hostsharing.hsadminng.service.dto.CustomerDTO;
import org.hostsharing.hsadminng.service.mapper.CustomerMapper; import org.hostsharing.hsadminng.service.mapper.CustomerMapper;
import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator; import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator;
@ -34,9 +35,12 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenAuthenticatedUser;
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenUserHavingRole;
import static org.hostsharing.hsadminng.web.rest.TestUtil.createFormattingConversionService; import static org.hostsharing.hsadminng.web.rest.TestUtil.createFormattingConversionService;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/** /**
* Test class for the CustomerResource REST controller. * Test class for the CustomerResource REST controller.
* *
@ -133,6 +137,10 @@ public class CustomerResourceIntTest {
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
givenAuthenticatedUser();
givenUserHavingRole(Role.ADMIN);
final CustomerResource customerResource = new CustomerResource(customerService, customerQueryService); final CustomerResource customerResource = new CustomerResource(customerService, customerQueryService);
this.restCustomerMockMvc = MockMvcBuilders.standaloneSetup(customerResource) this.restCustomerMockMvc = MockMvcBuilders.standaloneSetup(customerResource)
.setCustomArgumentResolvers(pageableArgumentResolver) .setCustomArgumentResolvers(pageableArgumentResolver)
@ -144,7 +152,7 @@ public class CustomerResourceIntTest {
/** /**
* Create an entity for this test. * Create an entity for this test.
* * <p>
* This is a static method, as tests for other entities might also need it, * This is a static method, as tests for other entities might also need it,
* if they test an entity which requires the current entity. * if they test an entity which requires the current entity.
*/ */
@ -170,7 +178,7 @@ public class CustomerResourceIntTest {
/** /**
* Create another entity for tests. * Create another entity for tests.
* * <p>
* This is a static method, as tests for other entities might also need it, * This is a static method, as tests for other entities might also need it,
* if they test an entity which requires the current entity. * if they test an entity which requires the current entity.
*/ */
@ -372,22 +380,22 @@ public class CustomerResourceIntTest {
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.[*].id").value(hasItem(customer.getId().intValue()))) .andExpect(jsonPath("$.[*].id").value(hasItem(customer.getId().intValue())))
.andExpect(jsonPath("$.[*].reference").value(hasItem(DEFAULT_REFERENCE))) .andExpect(jsonPath("$.[*].reference").value(hasItem(DEFAULT_REFERENCE)))
.andExpect(jsonPath("$.[*].prefix").value(hasItem(DEFAULT_PREFIX.toString()))) .andExpect(jsonPath("$.[*].prefix").value(hasItem(DEFAULT_PREFIX)))
.andExpect(jsonPath("$.[*].name").value(hasItem(DEFAULT_NAME.toString()))) .andExpect(jsonPath("$.[*].name").value(hasItem(DEFAULT_NAME)))
.andExpect(jsonPath("$.[*].kind").value(hasItem(DEFAULT_KIND.toString()))) .andExpect(jsonPath("$.[*].kind").value(hasItem(DEFAULT_KIND.toString())))
.andExpect(jsonPath("$.[*].birthDate").value(hasItem(DEFAULT_BIRTH_DATE.toString()))) .andExpect(jsonPath("$.[*].birthDate").value(hasItem(DEFAULT_BIRTH_DATE.toString())))
.andExpect(jsonPath("$.[*].birthPlace").value(hasItem(DEFAULT_BIRTH_PLACE.toString()))) .andExpect(jsonPath("$.[*].birthPlace").value(hasItem(DEFAULT_BIRTH_PLACE)))
.andExpect(jsonPath("$.[*].registrationCourt").value(hasItem(DEFAULT_REGISTRATION_COURT.toString()))) .andExpect(jsonPath("$.[*].registrationCourt").value(hasItem(DEFAULT_REGISTRATION_COURT)))
.andExpect(jsonPath("$.[*].registrationNumber").value(hasItem(DEFAULT_REGISTRATION_NUMBER.toString()))) .andExpect(jsonPath("$.[*].registrationNumber").value(hasItem(DEFAULT_REGISTRATION_NUMBER)))
.andExpect(jsonPath("$.[*].vatRegion").value(hasItem(DEFAULT_VAT_REGION.toString()))) .andExpect(jsonPath("$.[*].vatRegion").value(hasItem(DEFAULT_VAT_REGION.toString())))
.andExpect(jsonPath("$.[*].vatNumber").value(hasItem(DEFAULT_VAT_NUMBER.toString()))) .andExpect(jsonPath("$.[*].vatNumber").value(hasItem(DEFAULT_VAT_NUMBER)))
.andExpect(jsonPath("$.[*].contractualSalutation").value(hasItem(DEFAULT_CONTRACTUAL_SALUTATION.toString()))) .andExpect(jsonPath("$.[*].contractualSalutation").value(hasItem(DEFAULT_CONTRACTUAL_SALUTATION)))
.andExpect(jsonPath("$.[*].contractualAddress").value(hasItem(DEFAULT_CONTRACTUAL_ADDRESS.toString()))) .andExpect(jsonPath("$.[*].contractualAddress").value(hasItem(DEFAULT_CONTRACTUAL_ADDRESS)))
.andExpect(jsonPath("$.[*].billingSalutation").value(hasItem(DEFAULT_BILLING_SALUTATION.toString()))) .andExpect(jsonPath("$.[*].billingSalutation").value(hasItem(DEFAULT_BILLING_SALUTATION)))
.andExpect(jsonPath("$.[*].billingAddress").value(hasItem(DEFAULT_BILLING_ADDRESS.toString()))) .andExpect(jsonPath("$.[*].billingAddress").value(hasItem(DEFAULT_BILLING_ADDRESS)))
.andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK.toString()))); .andExpect(jsonPath("$.[*].remark").value(hasItem(DEFAULT_REMARK)));
} }
@Test @Test
@Transactional @Transactional
public void getCustomer() throws Exception { public void getCustomer() throws Exception {
@ -1162,8 +1170,8 @@ public class CustomerResourceIntTest {
// Disconnect from session so that the updates on updatedCustomer are not directly saved in db // Disconnect from session so that the updates on updatedCustomer are not directly saved in db
em.detach(updatedCustomer); em.detach(updatedCustomer);
updatedCustomer updatedCustomer
.reference(UPDATED_REFERENCE) .reference(null)
.prefix(UPDATED_PREFIX) .prefix(null)
.name(UPDATED_NAME) .name(UPDATED_NAME)
.kind(UPDATED_KIND) .kind(UPDATED_KIND)
.birthDate(UPDATED_BIRTH_DATE) .birthDate(UPDATED_BIRTH_DATE)
@ -1188,8 +1196,8 @@ public class CustomerResourceIntTest {
List<Customer> customerList = customerRepository.findAll(); List<Customer> customerList = customerRepository.findAll();
assertThat(customerList).hasSize(databaseSizeBeforeUpdate); assertThat(customerList).hasSize(databaseSizeBeforeUpdate);
Customer testCustomer = customerList.get(customerList.size() - 1); Customer testCustomer = customerList.get(customerList.size() - 1);
assertThat(testCustomer.getReference()).isEqualTo(UPDATED_REFERENCE); assertThat(testCustomer.getReference()).isEqualTo(DEFAULT_REFERENCE);
assertThat(testCustomer.getPrefix()).isEqualTo(UPDATED_PREFIX); assertThat(testCustomer.getPrefix()).isEqualTo(DEFAULT_PREFIX);
assertThat(testCustomer.getName()).isEqualTo(UPDATED_NAME); assertThat(testCustomer.getName()).isEqualTo(UPDATED_NAME);
assertThat(testCustomer.getKind()).isEqualTo(UPDATED_KIND); assertThat(testCustomer.getKind()).isEqualTo(UPDATED_KIND);
assertThat(testCustomer.getBirthDate()).isEqualTo(UPDATED_BIRTH_DATE); assertThat(testCustomer.getBirthDate()).isEqualTo(UPDATED_BIRTH_DATE);