5

I am creating a Rest API for a MongoDB database using MongoRepository. I want to create an endpoint that uses "RequestMethod.PATCH" and implements the "PATCH" functionality: delta update with fields provided in the @RequestBody.

The functionality that I want already exists in "Spring Data Rest" by using the "@RepositoryRestResource" annotation on my Repository class as described here https://spring.io/guides/gs/accessing-data-rest/

But I don't want to expose my Repository class like that. I like the classic Controller->Service->Repository lineage. My controller looks like this:

@RestController
public class ActivitiesController {

    @Autowired
    ActivitiesService activitiesService;

    @RequestMapping(value="activities", method=RequestMethod.PATCH)
    public ActivityModel updateActivity(
            @RequestBody ActivityModel activityModel
    ){
        //Input ActivityModel will only have subset of fields that have been changed, aka the delta
        return activitiesService.update(activityModel);
    }

    @RequestMapping(value="activities", method=RequestMethod.PUT)
    public ActivityModel updateActivity(
            @RequestBody ActivityModel activityModel
    ){
        //Input ActivityModel will have all fields populated
        return activitiesService.save(activityModel);
    }

}

And my repository is here:

@Repository
public interface ActivitiesRepo extends MongoRepository<ActivityModel, String> {
    //out of the box implementation
}

My problem is that, from what I can tell, MongoRepository does not provide delta updates out of the box the way that Spring Data Rest does. How can I implement that functionality in the Service layer here?:

@Service
public class ActivitiesService {

    @Autowired
    ActivitiesRepo activitiesRepo;

    public ActivityModel update(ActivityModel activityModel){
        //delta update implementation, aka PATCH implementation
    }

    //method that should only be used with RequestMethod.PUT
    public ActivityModel save(ActivityModel activityModel){
        return activitiesRepo.save(activityModel);
    }

}

What do you think of this solution for a manual "PATCH" implementation:

public class ModelUtil {
    public static <T> Object update(Object origModel, Object dirtyModel, Class<T> clazz){

        ObjectMapper m = new ObjectMapper();
        HashMap<String, Object> origModelAsMap = m.convertValue(origModel, new TypeReference<Map<String, Object>>() {});
        HashMap<String, Object> dirtyModelAsMap = m.convertValue(dirtyModel, new TypeReference<Map<String, Object>>() {});

        dirtyModelAsMap.forEach((k, v)-> {
            origModelAsMap.put(k, v);
        });

        return m.convertValue(origModelAsMap, clazz);
    }
}
Silas
  • 61
  • 1
  • 4
  • The problem is Spring MVC does not support patch: See http://stackoverflow.com/a/39424421/1356423 – Alan Hay Feb 01 '17 at 19:16
  • Yes, but any suggestion on how I can implements it in an elegant way in the Service bean? – Silas Feb 01 '17 at 21:16
  • Not easily. How will you differentiate fields explicitly nulled in the PATCH request against fields not present in the request for example? – Alan Hay Feb 01 '17 at 21:26
  • @AlanHay what do you think of the new code I added above for implementing a "PATCH" type update? – Silas Feb 02 '17 at 17:40

0 Answers0