Create a dedicated builder for HAL representations
See original GitHub issue@drdamour I’m experimenting on a fluent API to build RepresentationModel
objects. What do you think of something like this for building an embedded structure like you depicted up above?
@RestController
static class ProductController {
LinkRelation favoriteProducts = LinkRelation.of("favorite products");
LinkRelation purchasedProducts = LinkRelation.of("purchased products");
@GetMapping("/products")
public RepresentationModel<?> all() {
EmbeddedModelBuilder builder = ModelBuilder //
.embed() //
.rootLink(linkTo(methodOn(ProductController.class).all()).withSelfRel());
PRODUCTS.keySet().stream() //
.map(id -> new EntityModel<>(PRODUCTS.get(id), new Link("http://localhost/products/{id}").expand(id))) //
.forEach(productEntityModel -> {
if (productEntityModel.getContent().isFavorite()) {
builder //
.embed(favoriteProducts) //
.entityModel(productEntityModel) //
.rootLink(productEntityModel.getRequiredLink(SELF).withRel(favoriteProducts));
}
if (productEntityModel.getContent().isPurchased()) {
builder //
.embed(purchasedProducts) //
.entityModel(productEntityModel) //
.rootLink(productEntityModel.getRequiredLink(SELF).withRel(purchasedProducts));
}
});
return builder.build();
}
}
It yields:
{
"_embedded" : {
"favorite products" : [ {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/777"
}
}
}, {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/998"
}
}
} ],
"purchased products" : [ {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/111"
}
}
}, {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/222"
}
}
}, {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/333"
}
}
}, {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/444"
}
}
}, {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/555"
}
}
}, {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/666"
}
}
}, {
"someProductProperty" : "someValue",
"_links" : {
"self" : {
"href" : "http://localhost/products/998"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost/products"
},
"purchased products" : [ {
"href" : "http://localhost/products/111"
}, {
"href" : "http://localhost/products/222"
}, {
"href" : "http://localhost/products/333"
}, {
"href" : "http://localhost/products/444"
}, {
"href" : "http://localhost/products/555"
}, {
"href" : "http://localhost/products/666"
}, {
"href" : "http://localhost/products/998"
} ],
"favorite products" : [ {
"href" : "http://localhost/products/777"
}, {
"href" : "http://localhost/products/998"
} ]
}
}
A plain old collection where the embedded link relation is a based on the domain type:
@GetMapping("/authors")
RepresentationModel<?> collection() {
return ModelBuilder //
.collection() //
.entity(new Author("Greg L. Turnquist", null, null)) //
.link(linkTo(methodOn(EmbeddedController.class).authorDetails(1)).withSelfRel())
.link(linkTo(methodOn(EmbeddedController.class).collection()).withRel("authors")) //
.entity(new Author("Craig Walls", null, null)) //
.link(linkTo(methodOn(EmbeddedController.class).authorDetails(2)).withSelfRel())
.link(linkTo(methodOn(EmbeddedController.class).collection()).withRel("authors")) //
.entity(new Author("Oliver Drotbhom", null, null)) //
.link(linkTo(methodOn(EmbeddedController.class).authorDetails(2)).withSelfRel())
.link(linkTo(methodOn(EmbeddedController.class).collection()).withRel("authors")) //
.rootLink(linkTo(methodOn(EmbeddedController.class).collection()).withSelfRel()) //
.build();
}
produces
{
"_embedded" : {
"authors" : [ {
"name" : "Greg L. Turnquist",
"_links" : {
"self" : {
"href" : "http://localhost/author/1"
},
"authors" : {
"href" : "http://localhost/authors"
}
}
}, {
"name" : "Craig Walls",
"_links" : {
"self" : {
"href" : "http://localhost/author/2"
},
"authors" : {
"href" : "http://localhost/authors"
}
}
}, {
"name" : "Oliver Drotbhom",
"_links" : {
"self" : {
"href" : "http://localhost/author/2"
},
"authors" : {
"href" : "http://localhost/authors"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost/authors"
}
}
}
And a single item representation:
@GetMapping("/other-author")
RepresentationModel<?> singleItem() {
return ModelBuilder //
.entity(new Author("Alan Watts", "January 6, 1915", "November 16, 1973")) //
.link(new Link("/people/alan-watts")) //
.build();
}
produces
{
"name" : "Alan Watts",
"born" : "January 6, 1915",
"died" : "November 16, 1973",
"_links" : {
"self" : {
"href" : "/people/alan-watts"
}
}
}
Related issue: #175
Issue Analytics
- State:
- Created 5 years ago
- Comments:21 (13 by maintainers)
Top Results From Across the Web
Spring HATEOAS - Reference Documentation
As of Spring HATEOAS 1.1, we ship a dedicated HalModelBuilder that allows to create RepresentationModel instances through a HAL-idiomatic API.
Read more >Haley: A HAL Resource Builder in Java
Manually implementing the generation of HAL resources for REST/HATEOAS-based web services in Java can be tedious.
Read more >Spring RESTBucks in 2021 - Oliver Drotbohm
We start by introducing a dedicated data transfer object (DTO) to ... the affordance manually using Spring HATEOAS' AffordanceBuilder API.
Read more >Writing REST Services with RESTEasy Reactive - Quarkus
Designates a particular representation (also called a media type), ... You can also set the quarkus.resteasy-reactive.path build time ...
Read more >Computational tools for drawing, building and displaying ...
and representation of glycan structures in symbolic form by computational tools like GlycanBuilder [10]. Since then, several.
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Agree on user over developer.
So I seek maximum readability six months later.
Not necessarily. As I explained, there could be other more general methods on it but if you’re starting with “Here’s a link with a given relation…”, it’s quite natural to be immediately able to state “Oh, btw. here’s the preview for the resource that just mentioned link points to.”.