3

Premise:
A web-service exposes its data via REST. Each record belongs to the user who created the record (row-level-security). Users may only retrieve own records.

@RepositoryRestResource(path = "talks")
public interface TalkRepository extends PagingAndSortingRepository<Talk, Long> {

    @Override
    @Query("select t from Talk t where t.owner.id= ?#{principal?.id}")
    Page<Talk> findAll(Pageable pageable);
}

That repository is now available under a /talks endpoint.

Question:
Is there a way 1) to expose the same domain entity at multiple endpoints and 2) define different @Query annotations depending on the endpoint?

  • /talks
    I'd let that be my default implementation open to admin roles
  • /me/talks
    this is the endpoint that applies row level security against the principal and as part of the /me/** endpoints is exposed as public api to implementing clients.

This question is partially related to https://jira.spring.io/browse/DATAREST-555, but only in so far that the additional path segment is currently not supported.

Rationale:
I like the idea of not having to put too much conditional logic into SPeL queries like is owner or has_some_role (some examples here). Further it would become easy to protect the /me/** endpoints by different strategies than the default API (e.g. only /me/** might be subject to OAuth2).

1
  • 1
    Just write a controller, bind it to a URI and call the repository yourself? Commented Oct 11, 2015 at 11:10

2 Answers 2

2

If you know better/more concise solutions I am happy to accept other answers.

Based on the suggestion of @OliverGierke, the official docs and various other SO answers (also almost exclusively by Oliver, mostly 1, 2 and 3) I have implemented a custom controller to serve the endpoint.

This also enables projections on the custom endpoint and uses Spring Data REST's HATEOS assembler to provide the HAL+JSON output. What I haven't worked on so far is how to reuse the profile and alps logic that SDR provides out of the box but gets lost within the custom controller.

@BasePathAwareController
public class MyTalksController {

    private final TalkRepository repository;
    private final PagedResourcesAssembler pagedResourcesAssembler;

    @Autowired
    public MyTalksController(TalkRepository repo, PagedResourcesAssembler assembler) {
        repository = repo;
        pagedResourcesAssembler = assembler;
    }

    @RequestMapping(method = RequestMethod.GET, value = "/me/talks")
    @ResponseBody
    public PagedResources<?> getTalks(Pageable pageable, PersistentEntityResourceAssembler entityAssembler) {
        Page<Talk> talks = repository.meFindAll(pageable);
        return pagedResourcesAssembler.toResource(talks, entityAssembler);
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

do you know if something is possible without using PagedResourceAssembler?
0

Answering 1) — yes, possible as follows:

// skipping old requests for the sake of the backward compatibility with the clients
// just returning ok with HTTP status 200
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS, RequestMethod.DELETE},
                value = {"/lock", "/configure", "/cancel", "/initialize", "/register"})
public ApiResponse ok() {
    return ApiResponse.success();
}

Answering 2) — different query logic will naturally fit different endpoints, so there is a need to create a corresponding method for each one, I'd assume.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.