6

I'm attempting to add some additional business logic to the auto-generated endpoints from the RepositoryRestResource. Please see the code below:

Resource:

@RepositoryRestResource(collectionResourceRel="event", path="event")
public interface EventRepository extends PagingAndSortingRepository<Event, Long> {

}

Controller:

@RepositoryRestController
@RequestMapping(value = "/event")
public class EventController {

  @Autowired
  private EventRepository eventRepository;

  @Autowired
  private PagedResourcesAssembler<Event> pagedResourcesAssembler;

  @RequestMapping(method = RequestMethod.GET, value = "")
  @ResponseBody
  public PagedResources<PersistentEntityResource> getEvents(Pageable pageable,
      PersistentEntityResourceAssembler persistentEntityResourceAssembler) {

    Page<Event> events = eventRepository.findAll(pageable);

    return pagedResourcesAssembler.toResource(events, persistentEntityResourceAssembler);
  }
}

I've looked at the following two stackoverflow articles:

I feel like I am close, but the problem that I am facing is that:

return pagedResourcesAssembler.toResource(events, persistentEntityResourceAssembler);

returns an error saying:

"The method toResource(Page<Event>, Link) in the type PagedResourcesAssembler<Event> is not applicable 
 for the arguments (Page<Event>, PersistentEntityResourceAssembler)".

The toResource method has a method signature that accepts a ResourceAssembler, but I'm not sure how to properly implement this and I can't find any documentation on the matter.

Thanks in advance, - Brian

Edit

My issue was that I thought I could override the controller methods that are auto-created from @RepositoryRestResource annotation without having to create my own resource and resource assembler. After creating the resource and resource assembler I was able to add my business logic to the endpoint.

Resource:

public class EventResource extends ResourceSupport {
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

Resource Assembler:

@Component
public class EventResourceAssembler extends ResourceAssemblerSupport<Event, EventResource> {

  public EventResourceAssembler() {
    super(EventController.class, EventResource.class);
  }

  @Override
  public EventResource toResource(Event entity) {
    EventResource eventResource = createResourceWithId(entity.getId(), entity);
    eventResource.setName(entity.getName());
    return eventResource;
  }
}

Updated Controller:

@RepositoryRestController
@RequestMapping(value = "/event")
public class EventController {

  @Autowired
  private EventRepository eventRepository;

  @Autowired
  private EventResourceAssembler eventResourceAssembler;

  @Autowired
  private PagedResourcesAssembler<Event> pageAssembler;

  @RequestMapping(method = RequestMethod.GET, value = "")
  @ResponseBody
  public PagedResources<EventResource> getEvents(Pageable pageable) {
    Page<Event> events = eventRepository.findAll(pageable);

    // business logic

    return pageAssembler.toResource(events, eventResourceAssembler);
  }
}

The thing I don't like about this is that it seems to defeat the purpose of having a RepositoryRestResource. The other approach would be to use event handlers that would get called before and/or after the create, save, delete operations.

@RepositoryEventHandler(Event.class)
public class EventRepositoryEventHandler {

  @HandleBeforeCreate
  private void handleEventCreate(Event event) {
    System.out.println("1");
  }
}

There doesn't seem to be any events for the findAll or findOne operations. Anyways, both these approaches seem to solve my problem of extending the auto generated controller methods from RepositoryRestResource.

Community
  • 1
  • 1
bmclachlin
  • 177
  • 3
  • 10
  • 2
    http://stackoverflow.com/questions/21346387/how-to-correctly-use-pagedresourcesassembler-from-spring-data may provide some more information. – Jason Nov 07 '15 at 14:47
  • @Jason Thanks this link definitely helped step me in the right direction. – bmclachlin Nov 09 '15 at 18:57

2 Answers2

1

It requires a PagedResourcesAssembler, Spring will inject one for you if you ask.

public PagedResources<Foo> get(Pageable page, PagedResourcesAssembler<Foo> assembler) {
    // ...
}

In this case the resource is Foo. It seems in your case the resource you're trying to return is an Event. If that's so, I would expect your code to look something like:

private ResourceAssembler<Event> eventAssembler = ...;
public PagedResources<Event> get(Pageable page, PagedResourcesAssembler<Event> pageAssembler) {
    Event event = ...;
    return eventAssembler.toResource(event, pageAssembler);
}

You provide the ResourceAssembler<Event> that tells Spring how to turn Event into a Resource. Spring injects the PagedResourcesAssembler<Event> into your controller method to handle the pagination links. Combine them by calling toResource and passing in the injected pageAssembler.

The final result can be returned simply as a body as above. You could also use things like HttpEntity to gain more control over status codes and headers.

Note: The ResourceAssembler you provide can literally be something as simple as wrapping the resource, such as Event, with a Resource object. Generally you'll want to add any relevant links though.

Community
  • 1
  • 1
kab
  • 131
  • 4
  • Thanks for your response. I thought maybe it was possible to override the auto-generated endpoints from the RepositoryRestResource annotation without having to create an EventResource and an EventResourceAssembler. Think the best solution might be to use a class with the RepositoryEventHandler annotation and use the events that the REST exporter emits. This way I don't have to touch my EventRepository and I can add my business logic to these endpoints. – bmclachlin Nov 09 '15 at 19:04
1

To hack it you can use just PagedResourcesAssembler<Object> like:

@RequestMapping(method = RequestMethod.GET, value = "")
@ResponseBody
public PagedModel<PersistentEntityResource> getEvents(
    Pageable pageable,
    PersistentEntityResourceAssembler persistentAssembler,
    PagedResourcesAssembler<Object> pageableAssembler
) {
    return pageableAssembler.toModel(
        (Page<Object>) repository.findAll(pageable),
        persistentAssembler
    );
  }