-
Notifications
You must be signed in to change notification settings - Fork 889
Profile extensions #957
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
Profile extensions #957
Conversation
This enables the client to distinguish between different error conditions that share the same status code, by matching on the `type` URI. This is needed for the extension system, where the client needs to distinguish between the `profile` parameter not being supported by server, vs. the client providing insufficient profile extensions, both of which trigger a 415. This is the start of what I suggested in #683.
This doesn’t go into the details of how to use the profile parameter (and handle errors that result from sending it to 1.0 servers)—those details will be given in the extension section. But it updates the basic rules that all clients + servers must follow on all requests.
This commit does two things: 1. It mentions, in the first sentence, that the restrictions apply to query parameter _names_ (not their values, or name–value pairs). 2. It splits the text into two paragraphs (the requirements and the recommendations) which makes it easier to read, since both those chunks have a lot of bold all caps text.
This key will be used, for now, to assign a simple/pretty name to each used extension (identified by its longer URI). The key is called “mappings”, rather than “aliases” because, in the future, I suspect that not every key in this object will be an alias for (i.e. directly substitutable with) its value. Instead, I can imagine this object also holding, e.g., a mapping between relationship names and URIs that can be used as an extended link relation to capture each relationship.
This commit removes the `code` member, which is better served by the new `type` link. Type allows a superset of code's capabilities. In particular, application-specific values can still be used (just by replacing `code` with a `type` URI that the server controls), but `type` also allows the reuse of well-known error types—both those defined in the JSON API spec (which will be necessary in the next commit) and those used by, e.g., problem+json. Removing members from the definition of an error object is NOT a breaking change. Old APIs that send these members will still work with old clients, and new clients won’t choke either, as they’ll simply ignore the old (and no-longer-defined) members. And, because these values were never required, all clients had to be able to function without them anyway. [See also my inline note about allowing clients to continue to support old members, if they want.] However, making it illegal to produce certain error object members going forward has the advantage of simplifying an error format that’s redundant right now and a bit underspecified.
Clarifies a number of references and fixes a broken link in the errors section.
Make the `code` inherit its weight by default, so it can be used inline with bold text, but set its weight to 600 in particular when its within an h5 (since h5’s weight of 500 happens to be normal weight still for common monospace fonts, which only have two weights). Remove the `.highlight pre` font-family declaration, since it was never getting applied (the `.highlight pre` always contains a `code`, and that’s the font that was being used).
Note: the new MUST isn’t actually an incompatible change; it was already implicit in 1.0, because a JSON Pointer that doesn’t point to a value is an invalid pointer according to the RFC. I’m just calling this case out more clearly to prevent confusion, as arose when we were generating the error object examples for the examples page.
Each member of a links object is a "link". A link **MUST** be represented as | ||
either: | ||
<a href="#document-links-link" id="document-links-link"></a> | ||
Within this object, a link **MUST** be represented as either: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that there's an analogy between the text here and #946.
I faced a couple problems writing this section:
- With the profile key now holding multiple links, we ca no longer say "each value is a link".
- Because we haven't yet defined the relationship between
links
and RFC 5988, I didn't want to introduces a term for the key (i.e. I didn't want to say whether it's the links name or a relation).
So, given the above constraints, the wording in the PR was what I came up with.
Let's get this PR merged, and then we can figure out how to clarify things in #958. I'll link this thread there for reference.
Looking pretty good! The one thing I'm uncomfortable with is the idiosyncratic usage of the term "mapping". Usually a mapping (or a map) means a system that maps each element in a source set to an element in a target set. In JSON, an object would be a mapping. In mathematics, a function would be a mapping. Thus, the object that is now named A key inside A key–value pair inside |
Great job Ethan. It's a good start to integrate extensions. I'm a bit busy at the moment, but I want to give you a short feedback. Some notes:
Regarding the missing extensions error:
|
A lot of the discussion over at #915 revolved around the naming system, which should, ideally, have the following properties:
One obvious approach that I don't think was ever brought up is to prefix a global name whenever it appears inside an extension object: {
"aliases": {
"attribute-validation": "http://jsonapi.org/ext/attribute-validation",
"common-validators": "http://jsonapi.org/ext/validators",
},
"data": {
"type": "articles",
"id": "1",
"attributes": {"title": "Lorem ipsum"}
},
"attribute-validation": { // Appears in global scope -> no prefix
"articles": {
"title": {
"$common-validators": {"string": true} // Nested alias -> prefix "$"
}
}
}
} This wouldn't treat extension aliases any differently from officially defined members because those, too, would have to be designated somehow if they were to appear within extension data. |
Other responses...
First, a heads up that I'm still not sure whether mappings should exist at all—that depends on how far JSON API wants to go down the path of supporting hypermedia. I.e. if we want extended rels and CURIEs, then So, since
@ziege Yes, I think that's fair. I'll update the PR accordingly.
Good points. Let's revisit this if we end up sticking with the non a-z rule.
Thanks! I'll add this to the list of possible approaches :) Hopefully I'll have a chance to talk with the other editors soon and we can figure out the priorities (functional and aesthetic) & narrow down the naming options.
Right. In my proposal, though, each missing extension get's its own error (which is important because the extensions could be missing at different
Instead, the missing ext1 would have its own error object, and the other value would be:
And that's where I think it's unclear to readers... if there is only one missing extension (i.e. if ext1 isn't missing/necessary, so there's just a choice between adding ext2 or ext3), and the above value is all the user sees, it's a little unclear whether both ext2 and ext3 are missing, or just one of them. That's what I was hoping we could find a better key name/structure for. Not a huge deal, but something to think about... |
Quoting myself here...
I'm not so sure about this requirement anymore. Its origins were in the idea that very common extensions might eventually become kind of de facto standards and, hence, should also look like standard members. However, at the same time, there's the "once an extension, always an extension" principle that specifically aims to keep (even universally adopted) extensions distinct from base spec additions. These two ideas seem a bit contradictory. If maintaining an extension-introduced feature is permanently delegated to that extension, is there anything wrong with also permanently denoting the corresponding JSON member as such? Maybe not. The aversion to "X-prefixing" that resulted in RFC 6648 is about cases where authors unilaterally introduce experimental names that later may become standardized. Neither applies to the situation here, as long as we have a registration requirement and a commitment not to absorb extensions into the base spec. Given that a global prefixing scheme would immediately resolve the namespace-partitioning predicament, the option shouldn't be dismissed out of hand — except to leave the door open for assimilating extensions as true first-class members of the spec. Also, I'm thinking it would probably be a good idea to preserve some form of the registered key scheme originally envisaged. Something like:
Strongly encouraging all APIs to use the same key for the same extension would benefit everyone with no real downsides, so we should absolutely take the opportunity to do so. This is especially crucial in the absence of a global prefixing system. It seems pointless, even counterproductive, to make extension keys look like first-class members if their meaning can vary from server to server. |
The core team talked about this PR at today's meeting, so I wanted to give y'all some updates and a chance to weigh in on the new ideas. (These aren't yet reflected in the PRs text, which I'll update once there's consensus on the details.)
|
@ethanresnick Thanks for updating as per our discussion. I think we decided to use a top-level We didn't discuss using extensions within extensions specifically, but I don't see the need to use I imagine that the canonical home page example with a couple extensions might look like this:
|
Yeah, I remember you suggested that, I just wasn't sure whether we'd settled on it. My concern with calling the key For instance, if we want to support CURIE-like strings as {
"extensions": {
"pagination": "http://jsonapi.org/extensions/pagination",
"revision": "http://jsonapi.org/extensions/revisions",
"rel-prefixes": "http://jsonapi.org/extensions/extended-rels"
},
"meta": {
"rel-prefixes": {
"API": "http://mydomain.com/custom-rels/"
}
},
"links": {
"API/home": "http://mydomain.com/api/v2"
}
// ...
} (Above, the By contrast, if we called the {
"aliases": {
"pagination": "http://jsonapi.org/extensions/pagination",
"revision": "http://jsonapi.org/extensions/revisions",
"API": "http://mydomain.com/custom-rels/"
},
"links": {
"API/home": "http://mydomain.com/api/v2"
}
// ...
} That's why I've seen the
Just to make sure we're talking about the same model for nesting extensions within extensions, check out this comment, if you haven't already, to see what I have in mind. Do you agree with that basic approach? (Btw, here's the part of the PR that attempts to codify that model... it could maybe use some clarification.) So, how would this work without the |
Fair enough. It does seem unsettled after all.
We should consider using the top-level
I think we're going down the rabbit hole of complexity here. An extension must be defined by the URL associated with it, and its value can be opaque to the base json:api spec. I'm not convinced that we need rules for extension composition in the base spec. If a particular extension wants to yield to other extensions at a particular key, then that's its prerogative. The extension can write the rules of what's allowed for any key under its control. |
Big 👍 for using the |
Using Re how much control we need to exert over extension composition... I'm not sure... let's talk about it next week? |
@ethanresnick can you pull out your |
This will be used for a server to indicate which profile extension is missing from a client’s document, in the event that a server requires an extension be used (e.g. for authentication). Note: if the markdown formatting looks weird…it is, but that’s the only formatting that would put the “This object…” paragraph under the `missing` bullet.
8c50aee
to
4ca869c
Compare
Because aliases are now going to only show up within `meta`, they don’t need to contain a non-[a-z] character.
any updates on this and a general 1.1 roadmap? its is 11 months past the milestone in https://github.com/json-api/json-api/milestone/2. |
Add docs for at_least and at_most filters
Finally, draft text for a profile extension system! Take a look :)
A few notes/points for discussion, continuing threads from #915:
The
mappings
object. In this PR, each extension is assigned an alias in a separate"mappings"
object, rather than through analias
key (or similar) in its link object. While the latter design would be simpler right now, the argument in favor of the"mappings"
approach is that it would allow us to define other string to URI-or-link-relation mappings in the same object/with a unified system in the future (e.g. to support extended relations/CURIEs in link objects; see the ideas in Extensions next steps #915 (comment)). I'm not sure how important that is, though it might be reasonably important if we end up supporting actions through extended relations or generally mapping better to RFC 5988. Also in favor ofmappings
: I'm not a huge fan of there being a link object key (alias
) that's only allowed in profile links... the links object already seems to have more than enough cases/formats imo.Ignore the naming scheme. As discussed in detail previously, we need some way to carve out a space of keys for use as extension aliases, so that these aliases won't conflict with future base spec keys we want to add. There are a ton of options for how to do this and, for this PR, I specified "must contain a non-[a-z] character" as the pattern for discriminating between static keys and extension aliases. Think of this rule just as a placeholder, though. I know that it's not perfect, as @bintoro said in Extensions next steps #915, and it's not even my favorite approach. Its primary downside, I think, is that it makes multi-word key names impossible (unless the words have no separator), and that seems like too big a price to pay. But I've specified it in this PR anyway because it was easy/consistent (its already what we're using for query parameters), and because I want to talk with the other editors first before bothering to spec out another approach, since a lot of this naming stuff is subjective/aesthetic. So we'll talk it out at our next meeting.
The error object changes. See the commit messages about adding the
type
link and removing thecode
member; they provide some context on why this is needed and how it's acceptable from a compatibility POV. There are other options here, but the ones I've thought of aren't as good as this approach. Re themissing
member: I went with a string in theextension
,parameter
, andkey
members, supplemented by analternatives
member, when clearly the same functionality could've been achieved by dropping alternatives and simply allowingextension
,parameter
, andkey
to take an array. I.e., I could have done:I didn't do that, though, because I thought people would think that all the extensions in that array were missing, when the meaning is really that (at least) one of those extensions must be provided. Maybe there are some key names that make this unambiguous, and allow us to get rid of the extra
alternatives
key? (All the unambiguous key names I thought of required multiple words.)De-emphasizing
meta
. Once we have profile extensions in the spec, does the role ofmeta
change? I think it should, but I'm curious about y'alls take on exactly how.Extension registration. Long-term I'm pretty sure we want some sort of web app to allow people to browse extensions and submit a registration through a nice form. But the workflow described on the extensions page here was just meant to be the MVP: fill out a template and post it on discuss/Github.