New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Linking from complex attributes #383
Comments
Great analysis @ethanresnick. I like your "in-between" approach a lot! One aspect I've been trying to push for is uniformity of representations. It would be very elegant if a client didn't have to parse the same thing in different ways depending on the HTTP endpoint it happens to use. (This principle is not limited to complex attributes. I've brought this up elsewhere, too.) Complex attributes that have their own In the in-between proposal, the representation per se isn't uniform (because
Furthermore, a client shouldn't have to care whether Nice job! |
@bintoro Thanks! And I'm glad you're thinking about parsing uniformity. That's been an ongoing concern for many of us—see, e.g., #276—and I think the spec is much better now in this regard. I still haven't taken the time to wrap my head around all the new cases introduced by rc2, but I'm happy to hear that the in between solution appears to work with them. One thing, though: I'm not sure that (EDIT: remove discussion of GET /people/1/links, since that's an invalid request). The other request that I'm most worried about is |
In this particular case, sure, it may never be necessary to target When you think of it this way, a term like "embedded resource" or "subresource" might be more apt than "complex attribute". (EDIT: But for the purposes of the spec, I wouldn't favor such a term unless the structure was identical with "proper" resources, which it isn't in this proposal.)
I would expect {
"data": {
"links": ["employer", "address.city"],
"employer": {
"relationship": "http://example.com/people/1/employer",
"href": "http://example.com/companies/2",
"type": "companies",
"id": "2"
},
"address.city": {
"type": "cities",
"id": "123"
}
}
} In the "nested links bags" scheme, I would expect any links in the substructures to be completely excluded, simply because they are not, in fact, part of the By the way, I like that you've included both the relationship-specific resource URL as well as the canonical URL ( EDIT: I should add that, purely from a parsing point of view, I find it simpler to deal with the "nested links" format because the links are available at the relevant context. Is there some obvious use case for dealing with them all at once? I mean, if |
@bintoro Whoops, I totally didn't mean to put Re...
Can you explain this a bit? As I see it, there are cases where a separate resource makes sense semantically and other cases where what you want to specify is really a complex attribute. But I can't imagine one case changing into another. If we really do start thinking about embedded resources, a la HAL, I think stuff's gonna get messy really fast. |
I was thinking of something you said in #341:
It feels like an appealing property of a system if the representation of a resource can remain the same when it appears as a part of a larger resource. For example, you might take a snapshot of an independent, linked resource and permanently store it as part of some context. In this respect, the "nested links" route is more complete. I suppose you're right that actually requesting a wholly dependent subresource directly is mostly just a theoretical concern. Looking at this again, one thing that occurred to me is that the "in-between" approach has a property that might be dissatisfying to some: Now:
The in-between model:
So, it kind of breaks the correlation between URLs and the corresponding objects. I guess the choice between the alternatives depends at least partly on how useful it really is to enumerate all the relationships at the top level of the main resource (see the addendum I edited on my previous comment). |
@bintoro Let me back up a step. The reason I want to keep the concept of a "complex attribute", rather than moving to the notion of a "subresource", is that I think having both the notion of a "subresource" and an "included resource" will get confusing. For example, I think it'll make people ask why a request for That said, for consistency and easy parsing, I agree with you that it makes sense to structure "complex attributes" in the same way we structure resource objects. And, I agree with your points about the in-between model breaking a lot of nice correspondences between the URLs and the payload. So, now, I'm thinking that one option would be to stick with the nested-links approach—which has all the advantages for parsing and url–object correspondence—and simply prevent the concept of a complex attribute from collapsing into the concept of a subresource by saying explicitly that a complex attribute MUST NOT be accessible at its own url, in the same way that simple attributes (like The only problem I see with this is that, if the complex attribute doesn't have a url, how do you request the link object inside of it? The nested-links payload's structure would suggest So what are our other options? We could go back to your second proposal, but I agree that splitting up the attribute is suboptimal. A radical option would be to simply require the server to serialize the Alternatively, we could stick with the in-between approach, but change the url of Thoughts? Other options? |
Sorry, I was being unclear... I'm not proposing changing the term unless there's some particular reason. You said:
...so I used the term "subresource" to explain my thinking.
But what's the value in prohibiting it? The spec could just stay silent on the issue just like in the case of simple attributes.
By requesting If there's no need to request EDIT: Oh now I get it, you mean how to manipulate the relationships directly, like POST to a to-many. Well, you don't, you have to set them on the object. This is why I like the ability to address these things directly; then it happens in the usual way, e.g., EDIT 2: Hmm. I wouldn't actually find it atrocious at all if a server didn't serve |
Not being addressable is precisely the use case I want to support. Anyone who says 'you can just normalize' should go and do RDF tripples because thats where you end up. I don't want to implement a tripple store so I want attributes which have no identity in their own right but which may be conplex to be able to link out. There are countless basic data modeling use case drivers for this. As I've said previously the real problem stems from segregation of fields and links which has dubious justification, if any, at all. Its a handrail just like typed collection keys gave people comfort until they realized the handrail actually gets in the way. |
No one's saying complex attribute must be addressable. I just don't see the point of prohibiting it either. If someone wants to retrieve "1" by issuing And
If. But presumably people will want to do this, and there's a dearth of sensible options apart from making the nested |
@bintoro I just wanted to spell out that the major driver is for rich nested attributes is that they are not addressable as they don't have an id, and the fact the server does not support that level of granularity on fields. I do see a problem with interoperability with addressing "sub resources" / fields like you describe. Having clients support both styles where on some servers they may be able to address with fine granularity and others only at the owning resource is quite a different api. If however we limit 'jsob-api' defined addressibility to a resource, we still have the |
I understand the concern. Let's not veer off the main point because for such cases the spec already has it laid out: A valid JSON API document must consist of data objects. A wholly dependent complex attribute that doesn't have an ID cannot, own its own, constitute a valid data object. There is absolutely no way the base spec permits the kind of granular access you're worried about. However, what must be resolved is how to access the outbound links in such an attribute. For purposes of addressing, I don't see an issue with a complex attribute having a URL structure. The way I would go about it is to return a 204 or 403 at @ahacking, are you also opposed to this? (EDIT: as a recommendation — see next comment) @ethanresnick, the same principle applies to your modified proposal:
I don't think it implies anything. There's absolutely no rule saying all segments along a URL path must be directly accessible. We already have an example of this today. Fetching The bigger problem with the rename is that it reintroduces the "links"/"linked" confusion and leads to ugly URLs because you'd have to repeat "linked" at each level; consider |
One more thing: it's important to keep in mind that the resultant URL structure in the "nested links" scheme is nothing more than a logical extension of the recommended URL structure. The spec doesn't mandate any specific format, so an application can freely devise its own URL plan. For example, to expose all links directly under
|
@bintoro Your comments clear a bunch of things up. Between all the changes in RC2 and the fact that I've been out of this spec for a while until recently, I'm still reorienting myself to a bunch of the details. E.g. in the above discussion, I was operating under the assumption that Given, then, as you point out, that we already have some recommended urls for which dropping segments from the end produce an invalid url, I'm fine with this same principle applying to links from complex attributes. So we'd be recommending that So I think we just go with the nested links option. There's one more case I want to consider, though: the case where the complex attributes holds an array of objects, each of which links out. For example, take a slightly-simplified version of the
In this case, what are the recommended urls for those link objects? EDIT: We could build a url that uses the team member's index and, in REST terminology, that would be a sensible resource. (E.g. "/projects/1/team-members/0/links/person` would be the resource for "the link object for the person property of the first team member of project 1"). But, I'm not sure that such a url would be useful, given that the entity backing that resource is subject to change in unintuitive ways. So maybe we could recommend providing no relationship url at all? Or maybe we can find a scheme to recommend that better picks out the desired link object entity? |
Interesting example, @ethanresnick. My first instinct is the same, i.e. to use the array index. As for recommendations: since complex attributes may contain arbitrary structures, I think we should either refer to the JSON Pointer spec on how to construct the URL path or say nothing. Applying the JSON Pointer transformation would yield the same In any case, the discussion about addressing stuff inside complex attributes should definitely come with a clear warning about mutability. An index or a key is not a forever-unique ID. Good point there. Another thing I'd like the final spec to spell out more clearly is the principle that a client can't make any assumptions about URLs. If the team members'
Either way, no problem.
In the absence of IDs it's quite simply impossible to guarantee you're targeting the right entity. A client can fetch a link URL, but by the time it issues a request to it, the parent resource may have moved. Unfortunately there's nothing JSON API can do about it, so it's up to the application to prevent such race conditions somehow. |
@bintoro I totally agree with all that! JSON Pointer, plus a warning about mutability, sounds good. @dgeb I think @bintoro and I are in agreement here on how links from complex attributes should work. The final payload we're proposing looks like the below. Does this look good for submitting a PR? (I've used non-numeric ids below just to make it clearer what's linking to what without having to add comments.)
|
This could be an additive change post-1.0. |
But not very easily unless reserved keys are prefixed as suggested in #313. Linking from within complex attributes in the way proposed here means that |
Per our contribution guide, I am closing this discussion here because it is not on our roadmap for 1.1. I encourage you to continue this dialog @ http://discuss.jsonapi.org. We appreciate the suggestions greatly, but the core team only has so much bandwidth to deal with new feature requests. Because of this, we are focusing our efforts on empowering the community to create extensions without our direct involvement (until a critical mass of adoption makes it clear that something like this belongs in the base specification). |
/ref #794 |
Sounds like this is to be supported through an extension? Has anyone started on that? |
The use case
Consider the following cases:
"price"
attribute, which is an object of the form:{"amount": ..., "currency": ...}
, where the"currency"
property refers to a particularcurrencies
resource."team-members"
attribute, which is an array of objects of the form:{"person": ...., "role-played": ...}
, where the"person"
and"role-played"
properties refer to particularpeople
androles
resources.In both of the cases above, the attribute mentioned is an object or an array of objects that includes a link to another resource. But, crucially, the values for these attributes are only likely to occur in, or are only relevant in the context of, one resource. That is the (amount, currency) pair that represents a product's price doesn't make sense outside the context of that product, and the (person, role) pair that describes the role of a person on a particular project likely does not need to be considered outside of that project.
Accordingly, there's little semantic or functional justification for making separate, addressable resources from these "complex attributes"'s values. Meanwhile, turning them into separate resources can have a performance cost for backends implemented with document databases, which allow the complex attributes' values to be stored directly in the main resource's document. Therefore, the JSON-API spec shouldn't mandate that these complex attributes be turned into separate resources. But, if it's not going to mandate that, it has to somehow allow these complex attributes' links to other resources to be expressed in the response.
Prior Discussions
This issue has been discussed many times before, including in #238, #276, and #311. But many of the proposed solutions no longer apply post rc2. Post rc2, though, @bintoro made two proposals that offer good starting points for discussion.
My initial preferred approach
As I said previously, I think @bintoro's first proposal makes the a lot of sense. It's syntactically consistent with the current approach, and it seems easier to implement server-side than splitting up the complex attribute would be. Its downside, of course, is that it makes a parser's search for
"links"
somewhat more complicated.An in between option, that keeps it reasonably simple to find links but still keeps a complex attribute's value together, could be the following:
Here, the link objects have been treated just like any other attribute value, so links out from simple attributes are now at the top-level the resource. The
"links"
key just lists an array of paths that hold link objects.I have a feeling this might get strong pushback, though, as people seem to like the idea of the
"links"
key holding an object. I like that too, in theory, but I wonder whether, if the alternative is having multiple"links"
keys throughout the resource, the above isn't a better compromise. I don't know.Thoughts?
The text was updated successfully, but these errors were encountered: