Mason
Mason copied to clipboard
Discussion: is the "alt" property on links a good solution?
A link in Mason is always represented as a single object. If there are more links for the same link relation type (property name in the @links object) then they will be added as an array of link objects in the "alt" property:
"@links": {
// Primary link for "contact"
"contact": {
"href": "...",
"title": "Contact information",
"type": "application/vnd.mason+json",
// Alternate links for "contact"
"alt": [
{
href: "...",
"title": "Contact information (in vCard)",
"type": "text/vcard"
}
]
}
}
Is this a good solution?
Maybe the name should be more or additional?
Would this not be sufficient?
"@links": {
"contact": [{
"href": "...",
"title": "Contact information",
"type": "application/vnd.mason+json"
},{
"href: "...",
"title": "Contact information (in vCard)",
"type": "text/vcard"
}]
}
Yes, that is certainly sufficient. But ...
It means that simple clients always have to take index-zero of links which seems somewhat silly when 99% of the cases only have exactly one link. Instead of doing "body.links.link-rel-name.href" they have to do "body.links.link-rel-name[0].href".
I think I saw your name on the api-craft mailing-list where there were a reference to this discussion: https://github.com/kevinswiber/siren/issues/15
I am simply trying to make the simple stuff simple. And if clients are ready to test for more links of the same link-rel then they can do a "foreach" over the "alt" list.
See also page 31+32 in http://www.slideshare.net/pieterherroelen/angular-js-and-hal for some usage examples in HAL.
That totally makes sense. I'm new to this stuff, so pardon my naive questions.
You are welcome! That's why I ask those question in public.
How are you handling the "sometimes it's an array" problem that was recently discussed on the Hal forum?
[edit] Nevermind. Found the wiki on alt: The use of the "alt" property is an attempt to solve one of the debated features of HAL: links as either arrays or single objects."
Frankly, I'm not fond of this alt approach. The reason is consider the relation item. There is a whole RFC that describes both the item and collection relations. It is clear that the cardinality of item is zero or more. In this context, I see two issues with alt.
First (and assuming I understand alt), it seems "unbalanced" that that one item would be returned and a list of alternates. What's so special about that one item? What's wrong with all the others? One is given "first class" treatment and the others "second class". That just doesn't feel right.
Second, what if I want to express that a collection is empty? Sure, I could leave the item relation out. But I might prefer a style where I'm explicitly supplying an empty set of links via the item relation. I can do that in HAL, but not with this alt approach.
The question is what would work better. It seems to me the central issue here is that the client and the server need some a priori agreement on the cardinality of the relation. Imagine, just as an example, that you had both @link (for singleton/objects) and @links for zero or more. The assumption here is that the client, when traversing the API, either has the ability to deal with multiple relations or it doesn't. By making a request of @links, it is expressing "hey, I can handle an array". If it makes a request of @link it is saying "I'm only expecting one of these". A smart client side library could always "failover" in such a way that if a relation didn't appear in @links, it looked in @link and if it found a relation cast it into an array. With a convention like this, it seems like the client never has to worry about checking the cardinality itself. It just requests what it knows how to work with.
Another, more limited, way to deal with multiple items is to use URI templates. Imagine I have a bunch of resources that are all of the form: /invoices/{:id}. Instead of sending a list of hrefs, e.g., ["/invoices/23", "/invoices29", ...], you could simply provide the URI template and partial binding of parameters. Of course, this only works for cases where the links share an underlying pattern. It also doesn't give you unique titles, types, etc. per link. But it handles one class of cases (at the expense of having to complicate the specification with this partial binding information).
Anyway, just my thoughts.
Yes, "alt" may not be the best solution. There are certainly situations where it doesn't make sense to prioritize between a "primary" and multiple "secondaries".
See https://groups.google.com/forum/#!topic/hal-discuss/veh0QI9xlB8 for a similar discussion in the HAL forum.
Currently I haven't found a better solution. All have their different problems. But I am reconsidering it for version 2.
I've been mulling this over since I read the Mason spec.
On the one hand, I feel like we should have everyone use a client library or at least a gist they can copy into their app that deals with cardinality.
On the other hand, giving up simple dot dereferencing for the common case seems misguided. The prime example would be self where you know there is exactly one. I guess in HAL it's zero or one.
Can you think of an example where self would have alts?
"Alt" sounds like content type differences - jpg, png, gif or json, json+hal, json+mason. It appears that the use here is that, but it also might be "the car and the cdr of a list".
Fwiw, I agree with your statement on another forum that it's nicer to list the alternate content types rather than forcing another discovery check. That is especially true for first time users.
@kijanawoodard My point was that I think we could keep the convenience of single dot dereferencing if we can easily distinguish cardinality from the media format. In my example, if you know something is a singleton, you'd use res["link"][rel].href. If you knew it was (potentially) plural, you'd say res["links"][rel][i].href. Because these relations are separated by their potential plurality, there is no issue really.
But I think an equally valid approach would be to have smarter client libraries. In this case, we are talking about following relations. I think the best approach is not to use native JSON (res["links"][rel][0].href) but to have a document model that takes care of this behind the scenes, e.g., res.follow(rel).getUri() vs res.follow(rel, 0).getUri().
This is partly why I asked recently in the HAL discussion group if there was a standard client model. It might lead to a more restrictive interpretation of HAL (w.r.t. embedded resources, handling of links, etc). But I just think it would be nice to write client code as something like:
api.follow("service1").follow("search", {"term": "foo"}).follow("item", 0)
In Javascript, you can definitely take advantage of the dynamic typing to offer different variations of follow in this context. In the case of service1, clearly a singleton is expected. If an array is encountered, we throw an error. In the case of search, clearly template parameters are expected. If we don't fill all of them in, we throw an error. In the case of item, we take the first element in the array. If it happens to be a singleton (and since we've asked for 0), we return the singleton value (i.e., no error).
I think these are quite sane semantics for a client side API. They don't actually require you to distinguish cardinality of the relations because it is implicit in the way you "follow" the relations. Such a client library could check to make sure that there is no potential ambiguity as processes the links in the chain. It seems to me the only time this is really a problem is when people want to manipulate JS objects directly. I think that is fine for accessing parameters in the object. But I don't see why that should be so essential when dealing with links, embeddeds, hcp, etc. These are not native JS concepts so I don't think you can expect JS to be well aligned with them. That's where I think you need a client layer on top.
I'm with you on the client library. I'm using a library to make the http requests anyway. I may was well put one more layer in to help out.
For the case of "one day we woke up and realized there should be multiple", are you suggesting you would add an element to "links" and leave the "link" one in place using the first element? Or are you saying something else?
Just to be clear, the http library takes care of requests. I'm talking about a layer over responses as well.
But to your main point about "one day we woke up..." scenario. Fundamentally, what this means is that the API changed and a given relation is no longer a singleton. The key here, to me, is the API changed. Presumably the server changed its data model to respond with an array and now the client has to be consistent.
The first possibility is to use a different relation. But I'm going to assume you are using the same relation. Then the question is, how does this impact the code?
So if you have the explicit delineation (i.e., link vs. links), then the client needs to look for that relation under links. If, instead, you have a layer over JSON, then you'd end up changing res.follow("widget") to res.follow("widget", i). If course, this is language dependent. You can get away with such a simple change in JS if your client library features complicated argument handling for the overloading. In a statically typed language like Go, where I do a lot of my work, you'd probably end up changing res.Follow("widget") to res.FollowN("widget", i).
But an essential aspect of this is that with raw JSON manipulation (in Javascript), this change is going to result in something in the chain winding up being undefined and some puzzling downstream error message. But if you actually capture your intentions, the client library code can actually check your assumptions during processing and you'll get an intelligent error message like ("Relation 'widget' is a singleton but it was accessed as a collection").
"...the http library takes care of requests. I'm talking about a layer over responses as well." Right. We're saying the same thing. I'm already using one library here. Why am I suddenly against libraries?
Agree on capturing intent.
But I fear we're now OT for this Mason issue.
To be clear, I did not mean to imply we were in disagreement. I was just trying to emphasize the point (as you did) that if you use a library for requests, why not for responses too.
Warning: ramblings ahead ...
"Alt" sounds like content type differences - jpg, png, gif or json, json+hal, json+mason
Yes, it boils down to me having a specific, maybe wrong, understanding of multiple links of the same rel-type. I believe it comes from the ATOM link spec, but I cannot find it again, so I may easily be wrong. I remember it as saying "if there are multiple links of the same rel-type then they must differ on the expected media type" - strongly indicating that multiple links means different media types.
Then, from the HAL discussion on this issue, I realized that there may be other reasons for having multiple links of the same link-rel. For instance links to multiple stylesheets as used in HTML. This has now made me question the idea of using the "alt" approach.
What this means is that we have different ways of interpreting links of the same rel-type: it can be links to alternate representations of the same resource or it can be links to multiple different resources.
I am not a big fan of links to multiple resources because it can be represented as a plain JSON array of items with "self" links to the individual resources. But I do recognize that it may be useful in some simple scenarios.
Here is how I would encode multiple links to different resources - using as example a collection of items:
{
items:
[
{
id: 1234,
title: "Item A",
weight: 15,
@controls:
{
self: { href: "http://api.com/items/1234" }
}
},
{
id: 4321,
title: "Item B",
weight: 22,
@controls:
{
self: { href: "http://api.com/items/4321" }
}
}
]
}
Using arrays like this allows the server to embed all sorts of partial data from the linked resource without trying to cram it into the @controls items.
Please be aware that this is different from HAL which would expect you to use _embedded for this purpose.
For the above reasons it may just be that the "alt" approach is valid - but obviously not what people are expecting which certainly is a problem :-)
Imagine, just as an example, that you had both @link (for singleton/objects) and @links for zero or more
That won't be a viable way as Mason v2 unifies links, link templates and actions in a single @controls object. See https://github.com/JornWildt/Mason/blob/master/Documentation/Mason-draft-2.md#property-name-controls.
On the other hand, giving up simple dot dereferencing for the common case seems misguided. The prime example would be self where you know there is exactly one. I guess in HAL it's zero or one.
Can you think of an example where self would have alts?
No, but I have a similar example - the "author" rel-type. It identifies links to the author of the page (see http://www.w3.org/TR/html5/links.html#link-type-author). You may start out with a single "author" link because you only have a single author representation. But later on you figure out that you have both Mason, vCard, jCard and xCard representations - and that works very well with "alt" approach as these can simply be added as links to alternate representations without breaking clients that only depends on the primary link.
So - if one insists on multiple links of the same link-rel means variations of the same resource (like I tend to, but I'm still not sure) then "alt" seems pretty good. If you are in the other camp where multiple links can refer to different resources then, well, "alt" is not perfect - but, as shown above, you can
But to your main point about "one day we woke up..." scenario. Fundamentally, what this means is that the API changed and a given relation is no longer a singleton. The key here, to me, is the API changed.
Unless we simply added links to different representations of the same resource (as "alt" links).
But I also agree with you. If the API, as an example, goes from using one single address for a customer to having many addresses for a customer then it would be a breaking change.
I'm with you on the client library.
So am I.
I am working on one for C# with Ramone (which already has rudimentary support for selecting links and following them as well as invoking key/value forms from HTML). See https://github.com/JornWildt/Ramone.
This is partly why I asked recently in the HAL discussion group if there was a standard client model
I also believe there should be a standard client model/processing-rules and started work on that in Mason v2 (see https://github.com/JornWildt/Mason/blob/master/Documentation/Mason-draft-2.md#client-processing-rules).
I should add something on how to handle null/empty-list/singular/plural links ... when we have straighten out what it means :-)
*** CONCLUSION ***
Right now I am leaning towards the existing "alt" approach where "alt" means alternate links to the same resource represented in different ways. In this world the server should represent links to multiple resources (a collection of items) as a plain JSON array with multiple "self" links:
{
items:
[
{
id: 1234,
title: "Item A",
weight: 15,
@controls:
{
self: { href: "http://api.com/items/1234" }
}
},
{
id: 4321,
title: "Item B",
weight: 22,
@controls:
{
self: { href: "http://api.com/items/4321" }
}
}
]
}
When you mentioned Authors I thought your example was going to be the there is an article resource where there is originally one author, but later a second one does enough work to get an author credit.
Are you saying that you would start with an "authors" rel that is always an array and that , if you had started with "author" that would just have to be a breaking change?
Or are you saying that you would tend to put linked resources in "items" with a self link? Do the items in your example also need another "rel" so the client can figure out what they are before de-referencing them?
Or maybe those are api:items, not https://tools.ietf.org/html/rfc6573?
When you mentioned Authors I thought your example was going to be the there is an article resource where there is originally one author, but later a second one does enough work to get an author credit.
It almost started that way, but, no, I do not mean multiple authors - it mean multiple representation of a single author.
If the API began including multiple authors then I would consider it a breaking change.
Here is an example of the breaking change (representing a single book as example). First a single author:
{
title: "Story no. 1",
author:
{
name: "Jerry MacDonald",
@controls:
{
self: { href: "..." }
}
}
}
Then multiple authors (as a breaking change):
{
title: "Story no. 1",
author:
[
{
name: "Jerry MacDonald",
@controls:
{
self: { href: "..." }
}
},
{
name: "Liza Knutson",
@controls:
{
self: { href: "..." }
}
}
]
}
To avoid the breaking change I would do like this (although its a pain, so it would be weighted against the cost of upgrading all clients, and I would certainly deprecate "author" so it could be removed at some future date):
{
title: "Story no. 1",
author:
{
name: "Jerry MacDonald",
@controls:
{
self: { href: "..." }
}
},
authors:
[
{
name: "Jerry MacDonald",
@controls:
{
self: { href: "..." }
}
},
{
name: "Liza Knutson",
@controls:
{
self: { href: "..." }
}
}
]
}
What I was speaking of was using "alt" to link to multiple representations of the same author:
{
title: "Story no. 1",
author:
{
name: "Jerry MacDonald",
@controls:
{
self:
{
href: "...",
alt:
[
{ href: "...", output: ["application/json+jcard"] },
{ href: "...", output: ["text/vcard"] }
]
}
}
}
}
Are you saying that you would start with an "authors" rel that is always an array
No, I would always start with "author" (singular) as that is a IANA registed rel-type.
and that , if you had started with "author" that would just have to be a breaking change?
See the examples above.
It's unfortunate that this "just works" in XML but doesn't in json.
However, even with xml, it might be breaking anyway. It just won't break accessing the "resource object". Assume the xml always shows one author. One day there's a book with multiple.
If they design assuming there is only one ["put THE author in this space on the UI"] multiple authors would either get ignored or might break the ui,
It's unfortunate that this "just works" in XML but doesn't in json
Well, the programming languages I know of has to make the distinction one way or another when deserializing the XML (into arrays or single objects). So, yes, the syntax supports it out of the box - but I don't think you can work with the document tree/xpath/deserializer without taking the plurality into account, somehow.
Fair point.
If they design assuming there is only one ["put THE author in this space on the UI"] multiple authors would either get ignored or might break the ui,
Yeah, we can go through all sorts of hoops to be backwards-compatible - but there is a price to be paid somewhere no matter what.