Skip to content

Commit

Permalink
Use Jackson instead of reflection
Browse files Browse the repository at this point in the history
We can then levegrae all converters
  • Loading branch information
cgendreau committed Jan 6, 2025
1 parent a3f327a commit a8b9d0f
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ public interface DinaMapperV2<D, E> {
* Patch an existing entity from a DTO and a set of provided fields.
* Always set @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
* on the interface extending that one. Otherwise, non-provided values will be set to null.
* @param entity entity instance to be patched
* @param dto
* @param provided provided properties so only those will be set
* @param entity entity instance to be patched
* @param scope used to check provided properties within nested properties
*/
void patchEntity(D dto, @Context Set<String> provided, @MappingTarget E entity, @Context String scope);
void patchEntity(@MappingTarget E entity, D dto, @Context Set<String> provided, @Context String scope);

/**
* Used to map the uuid of an {@link ExternalRelationDto}.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
package ca.gc.aafc.dina.repository;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.info.BuildProperties;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.RepresentationModel;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tennaito.rsql.misc.ArgumentParser;
import com.toedter.spring.hateoas.jsonapi.JsonApiModelBuilder;

import ca.gc.aafc.dina.dto.ExternalRelationDto;
import ca.gc.aafc.dina.dto.JsonApiDto;
import ca.gc.aafc.dina.dto.JsonApiExternalResource;
import ca.gc.aafc.dina.dto.JsonApiMeta;
import ca.gc.aafc.dina.dto.JsonApiPartialPatchDto;
import ca.gc.aafc.dina.dto.JsonApiResource;
import ca.gc.aafc.dina.dto.JsonApiDto;
import ca.gc.aafc.dina.entity.DinaEntity;
import ca.gc.aafc.dina.filter.DinaFilterArgumentParser;
import ca.gc.aafc.dina.filter.EntityFilterHelper;
Expand All @@ -30,26 +30,24 @@
import ca.gc.aafc.dina.service.AuditService;
import ca.gc.aafc.dina.service.DinaService;

import static com.toedter.spring.hateoas.jsonapi.JsonApiModelBuilder.jsonApiModel;

import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletRequest;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;

import static com.toedter.spring.hateoas.jsonapi.JsonApiModelBuilder.jsonApiModel;

@Log4j2
public class DinaRepositoryV2<D,E extends DinaEntity> {

Expand All @@ -66,6 +64,7 @@ public class DinaRepositoryV2<D,E extends DinaEntity> {

protected final DinaMappingRegistry registry;

protected ObjectMapper objMapper;
private final ArgumentParser rsqlArgumentParser = new DinaFilterArgumentParser();

public DinaRepositoryV2(@NonNull DinaService<E> dinaService,
Expand All @@ -86,6 +85,10 @@ public DinaRepositoryV2(@NonNull DinaService<E> dinaService,

// build registry instance for resource class (dto)
this.registry = new DinaMappingRegistry(resourceClass);

// copy the object mapper and set it to fail on unknown properties
this.objMapper = objMapper.copy()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
}

/**
Expand Down Expand Up @@ -497,41 +500,31 @@ private static int toSafePageLimit(Integer pageLimit) {
}

/**
* WORK-IN-PROGRESS
* Update the resource defined by the id in {@link JsonApiPartialPatchDto} with the provided
* attributes.
* @param patchDto
*/
public void update(JsonApiPartialPatchDto patchDto) {

// we need to use reflection for now here since MapStruct doesn't support Map<String, Object> yet
try {
D dto = resourceClass.getConstructor().newInstance();
// We need to use Jackson for now here since MapStruct doesn't support setting
// values from Map<String, Object> yet.
// Reflection can't really be used since we won't know the type of the source
// and how to convert it.
D dto = objMapper.convertValue(patchDto.getMap(), resourceClass);

// validation of first level properties name
Set<String> receivedPropsName = patchDto.getPropertiesName();
Set<String> declaredPropsName = BeanUtils.describe(dto).keySet();

if(!declaredPropsName.containsAll(receivedPropsName)) {
throw new IllegalArgumentException("Property not found");
}

// Currently not working: Populate Dto to make sure data types are matching
BeanUtils.populate(dto, patchDto.getMap());

// load entity
E entity = dinaService.findOne(patchDto.getId(), entityClass);
if(entity == null) {
throw new IllegalArgumentException("not found");
}
// load entity
E entity = dinaService.findOne(patchDto.getId(), entityClass);
if (entity == null) {
throw new IllegalArgumentException("not found");
}

// apply DTO on entity using the keys from patchDto
dinaMapper.patchEntity(dto, receivedPropsName, entity, null);
// Check for authorization on the entity
authorizationService.authorizeUpdate(entity);

dinaService.update(entity);
// apply DTO on entity using the keys from patchDto
dinaMapper.patchEntity(entity, dto, patchDto.getPropertiesName(), null);

} catch (InstantiationException | InvocationTargetException | IllegalAccessException |
NoSuchMethodException e) {
throw new RuntimeException(e);
}
dinaService.update(entity);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ public interface ChainMapper extends DinaMapperV2<ChainDto, Chain> {
ChainDto toDto(Chain entity, @Context Set<String> provided, @Context String scope);

@Mapping(target = "chainTemplate", ignore = true)
void patchEntity(ChainDto dto, @Context Set<String> provided, @MappingTarget Chain entity, @Context String scope);
void patchEntity(@MappingTarget Chain entity, ChainDto dto, @Context Set<String> provided, @Context String scope);

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.mapstruct.BeanMapping;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
import org.mapstruct.MapperConfig;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValuePropertyMappingStrategy;
Expand All @@ -26,7 +25,7 @@ public interface PersonMapper extends DinaMapperV2<PersonDTO, Person> {

@Mapping(target = "department", ignore = true)
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void patchEntity(PersonDTO dto, @Context Set<String> provided, @MappingTarget Person entity, @Context String scope);
void patchEntity(@MappingTarget Person entity, PersonDTO dto, @Context Set<String> provided, @Context String scope);

@Mapping(target = "customField", ignore = true)
EmployeeDto employeeToDto(Employee entity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -126,7 +127,10 @@ public void onUpdate() {
JsonApiPartialPatchDto dto = new JsonApiPartialPatchDto();
dto.setId(person.getUuid());
dto.set("name", "abc");
dto.set("room", "a");
dto.set("room", 21);
// convert to string to mimic how we would get it with JsonApiPartialPatchDto
dto.set("createdOn", OffsetDateTime.now().toString());

repositoryV2.update(dto);
}

Expand Down

0 comments on commit a8b9d0f

Please sign in to comment.