spring-projects / spring-hateoas

Spring HATEOAS - Library to support implementing representations for hyper-text driven REST web services.
https://spring.io/projects/spring-hateoas
Apache License 2.0
1.03k stars 475 forks source link

Traverson: Allow access of intermediate resources when traversing relations #266

Open dantheperson opened 9 years ago

dantheperson commented 9 years ago

When using Traverson, i can only retrieve the resource at the end of the traversal. It is often useful to retrieve the intermediate objects.

For instance i have CREDENTIALS, which are linked to an IDENTITY, which has a (main) CONTACT which has some ROLEs.

For a credential, i want to retrieve the identity and its contacts roles.

This gives me the contact roles, but there is no way to get at the identity.

        Traverson t = new Traverson(new URI("http://127.0.0.1:8090/"), MediaTypes.HAL_JSON);
        t.setRestOperations(template);
        Map<String, Object> params = new HashMap<>();
        params.put("name", "V6UqkSG8");
        params.put("type", "myType");
        String contactUuid = t.follow("credentials", "search", "byNameAndType", "$_embedded.credentials[0]._links.identity.href", "mainContact", "roles")
                .withTemplateParameters(params).<String> toObject(String.class);

I can retrieve both this way, but's it's ugly.

        Traverson t = new Traverson(new URI("http://127.0.0.1:8090/"), MediaTypes.HAL_JSON);
        t.setRestOperations(template);
        Map<String, Object> params = new HashMap<>();
        params.put("name", "V6UqkSG8");
        params.put("type", "myType");
        String identity = t.follow("credentials", "search", "byNameAndType", "$_embedded.credentials[0]._links.identity.href")
                .withTemplateParameters(params).toObject(String.class);

        LinkDiscoverer linkDiscoverer = new HalLinkDiscoverer();
        Link contactLink = linkDiscoverer.findLinkWithRel("mainContact", identity);

        t = new Traverson(new URI(contactLink.getHref()), MediaTypes.HAL_JSON);
        t.setRestOperations(template);
        String contactRoles = t.follow("roles").<String> toObject(String.class);

A few options 1) Traverson could be stateful, retrieving an object and remembering it to allow a subsequent follow to resume from that resource

        String identity = t.follow("credentials", "search", "byNameAndType", "$_embedded.credentials[0]._links.identity.href").toObject(String.class);
        String contactRoles = t.follow("mainContact", "roles").toObject(String.class);

2) Traverson could store everything for later retrieval

        String contactRoles = t.follow("credentials", "search", "byNameAndType", "$_embedded.credentials[0]._links.identity.href", "mainContact", "roles").toObject(String.class);
        String contact = t.retrieveFromTraverse("mainContact").toObject(String.class);
        String identity = t.retreiveFromTraverse("$_embedded.credentials[0]._links.identity.href").toObject(String.class);

3) Traverson could optionally return some sort of intermediate object to hold state

        TraversonState identityTS = t.follow("credentials", "search", "byNameAndType", "$_embedded.credentials[0]._links.identity.href").toState();
        String identity = identityTS.toObject(String.class);
        String contactRoles = identityTS.follow("mainContact", "roles");

All much clearner looking than

        String identity = t.follow("credentials", "search", "byNameAndType", "$_embedded.credentials[0]._links.identity.href")
                .withTemplateParameters(params).toObject(String.class);

        LinkDiscoverer linkDiscoverer = new HalLinkDiscoverer();
        Link contactLink = linkDiscoverer.findLinkWithRel("mainContact", identity);

        t = new Traverson(new URI(contactLink.getHref()), MediaTypes.HAL_JSON);
        t.setRestOperations(template);
        String contactRoles = t.follow("roles").<String> toObject(String.class);

PS. It would be nice if spring-data-rest would return a single object from a query method whoose signature returns a single entity to avoid all the "$_embedded.credentials[0]._links.identity.href" stuff.

greggulrajani commented 7 years ago

Any comments / patches ?-- Was disappointed that the client library did not support this functionality. Seems like table stakes for Hypermedia consumption (and is the way Traverson works)

Will look into extending the library to support this functionality if it can be added cleanly ie continue()