Open samihus opened 4 years ago
According to the HAL FORMS spec, _templates
is a top-level property.
According to the HAL FORMS spec,
_templates
is a top-level property.
True, but in a HAL FORMS Document. If we consider that HAL FORMS document is identified by the relation in the Link object of the HAL document, then inlining it should be under the link object it refers to. Not at a global level...
why not include more than one element in _templates?
{
"_links: {...},
"_templates" : {
"self" : { ... },
"assignCommand" : { ... },
"resolveCommand" : { ... },
"cancelCommand" : { ...}
}
}
why not include more than one element in _templates?
Because the actions are performed on different URIs, I didn't found in the specification any reference inside the template object to a target URI. So I assumed it implicitly refers to the HAL Link object's URI.
you can use the href
that appears in the associated link
object inline.
{
"_links: {...},
"assignCommand" : { "href" : "/issues/1/assign-commands", ...},
"resolveCommand" : { "href" : "/issues/1/resolve-commands", ...},
"cancelCommand" : { "href" : "/issues/1/assign-commands", ...}
}
"_templates" : {
"self" : { ... },
"assignCommand" : { ... },
"resolveCommand" : { ... },
"cancelCommand" : { ...}
}
}
@odrotbohm : i assume what i am describing here is possible w/ the spring implementation. is that right?
you can use the href that appears in the associated link object inline
Do you mean that using the same name for the template
key and the link
key makes the mapping between the HAL Link Object and the JSON-FORMS Template Object ?
IOW, if I find assignCommand
in the links, then to know how to invoke it I should look at the assignCommand
template ?
yep. that's how it works for stand-alone HAL-FORMS, too. or you can use the _htarget
query string value to add/override that.
@mamund Currently, Spring HATEOAS's implementation looks for the self link, and from there looks for all "affordances". So no, we can't assemble multiple templates if that template is based on a different URI.
This was based on the fact that all the flows in the spec showed the self relation being the central one. Along with:
For this release, the only valid link object is the self link object.
We can certainly adjust things to look for affordance amidst ALL the links.
Inside HalFormsTemplateBuilder
, if I replace...
List<Affordance> affordances = resource.getLink(IanaLinkRelations.SELF) //
.map(Link::getAffordances) //
.orElse(Collections.emptyList());
...with...
List<Affordance> affordances = resource.getLinks().stream()
.flatMap(link -> link.getAffordances().stream())
.collect(Collectors.toList());
...then this...
RepresentationModel<?> model = new RepresentationModel<>();
Link selfLink = Affordances.of(Link.of("/issues/1")) //
.afford(HttpMethod.PUT) //
.withName("update") //
.toLink();
Link assignCommand = Affordances.of(Link.of("/issue/1/assign-commands", LinkRelation.of("assignCommand")))
.afford(HttpMethod.POST) //
.withName("assignCommand") //
.toLink();
Link resolveCommand = Affordances.of(Link.of("/issue/1/resolve-commands", LinkRelation.of("resolveCommand")))
.afford(HttpMethod.POST) //
.withName("resolveCommand") //
.toLink();
Link cancelCommand = Affordances.of(Link.of("/issue/1/cancel-commands", LinkRelation.of("cancelCommand")))
.afford(HttpMethod.POST) //
.withName("cancelCommand") //
.toLink();
model.add(selfLink, assignCommand, resolveCommand, cancelCommand);
System.out.println(this.mapper.writeValueAsString(model));
...will yield this HAL-FORMS document...
{
"_links" : {
"self" : {
"href" : "/issues/1"
},
"assignCommand" : {
"href" : "/issue/1/assign-commands"
},
"resolveCommand" : {
"href" : "/issue/1/resolve-commands"
},
"cancelCommand" : {
"href" : "/issue/1/cancel-commands"
}
},
"_templates" : {
"default" : {
"method" : "put",
"properties" : [ ]
},
"cancelCommand" : {
"method" : "post",
"properties" : [ ]
},
"resolveCommand" : {
"method" : "post",
"properties" : [ ]
},
"assignCommand" : {
"method" : "post",
"properties" : [ ]
}
}
}
This last change looks very promising. The result seems to match Mike Amundsen's design. Do you know if it will be featured into an upcoming release? I built the project with the fix. Surprisingly, no unit test was broken. Do you need help with these?
Oh, it wasn't clear to me at all from the HAL-FORMS specification that each _template
is supposed to correspond to a specific _link
. My understanding was that the target URL of the HTTP request defined by a template is supposed to be defined in the target
property of the template, or will fall back to the document's self
link otherwise.
In other words, when writing a client, I see no incentive to assume there might be a link with a rel matching the command I'm using, when there are the target
attribute and the self link that serve as fallback system. If there is a matching link as well as a target
property, how would I know which one should be used?
Ah, I see the target property has been implemented in the meantime in https://github.com/spring-projects/spring-hateoas/commit/752b000e6c7cc3dd3d5e03ed012dee87857117f4 for issue #1427. This means that this issue is more or less obsolete, correct?
The HAL-FORMS specification do not use the inlining mecanism, and rather recommends separating HAL and HAL FORMS documents. If I understood the implementation by Spring HATEOAS in this example, I think it reduces the features that can be offered by an API.
Let's consider an example with a resource "issue" (/issues/{id}). When performing a GET, I want to see its details + some hypermedia controls to tell me how to "update the details", how to "assign" it, how to "cancel" it and how to "mark it resolved". And I want all this actions to be performed in a "command style" resources and not as direct updates of the "issue" resource.
The actual implemented iniling do not allow this design... because there is no way to reference an URI from the _template, it's all implicitly linked to the self relation.
If the template object was inlined into a link object, then it would be possible to do that.