-
Notifications
You must be signed in to change notification settings - Fork 890
Introduce Profiles to v1.1 #1268
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
Conversation
Profiles are a means to describe additional semantics the JSON API media type, without altering the basic semantics. Profiles are identified by URI, which ideally should dereference to a document that describes the semantics of the profile. One or more profiles may be associated with the JSON API media type through the `profile` media type parameter. The application of profiles to a particular document can be specified by clients and servers via the `Content-Type` header. The application of one or more profiles to a response can be requested by a client via the `Accept` header. In order to require the application of one or more profiles, the profile(s) must be specified with the `profile` query parameter. Any structural elements introduced by a profile can be aliased. This allows for better adaptability and composition of profiles. A new profile descriptor object is introduced which allows for declarations of aliases in a particular document. Closes #1207
E.g., clarify that profiles can cover any implementation-specific query params, not just sort/filter/page
This is key for servers that don’t want to add `Vary: Accept` for better caching
Should be obvious, but worth saying
The link to the w3c tag terminology about backwards and forwards compatibility is more precise than “evolve additively”, though it doesn’t change the intended meaning
First off: thanks to everyone for this epic work, in this issue and related issues! I'm reviewing this as one of the people doing the work to bring JSON API support to Drupal core. Note that there are already >5000 Drupal 8 sites using JSON API (that usage count is an underestimation: it's the lower bound). As mentioned in #1207, we're working to minimize/reduce risk as much as possible, by having detailed attention to API surface and its implications: backwards compatibility, maintainability, supportability. We managed to achieve zero configuration: install this module and all Drupal data is instantly exposed via JSON API, respecting the existing access control logic. If users can view Specifically, the https://www.drupal.org/project/jsonapi module for Drupal (that we're working to move into Drupal core itself) uses two extensions: "Fancy Filters" and "Partial Success" — see https://www.drupal.org/project/jsonapi/issues/2955020. Without the former, no querying of resources (e.g. only published articles written by Wim), without the latter, impossible to do per-resource access control (e.g. Wim can see unpublished articles, but Gabe can't). Today, and for the past ~2 years, those extensions are always running, but their existence is not communicated explicitly, because the JSON API 1.0 spec does not support extensions, per http://jsonapi.org/extensions/. Therefore this issue/PR is essential for JSON API support to land in Drupal core. Otherwise we risk future JSON API specs moving in directions incompatible with what Drupal's JSON API implementation does … and then we would have a massive backwards compatibility problem. @effulgentsia, @gabesullice and I commented in #1207. I wanted to call out a two specific concerns they raised, and whether this PR addresses them:
This addresses @effulgentsia's concern in #1207 (comment). This would mean no BC break in the current
I don't think this concern is fully addressed? I think the conflict @gabesullice explained/predicted there still exists. I can understand that his Looking forward to your thoughts about this :) Review of the actual PR to follow. |
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.
Several nitpicks, but also some actual questions.
_format/1.1/index.md
Outdated
object. But it could not make that member required, nor could it introduce | ||
a new sibling to `timestamps`. | ||
|
||
Profiles **SHOULD** reserve at least one object-valued member, and **SHOULD** |
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.
This comes as a surprise to me. This means that a spec like Drupal's Fancy Filters is violates this recommendation. OTOH, Partial Success would comply (it reserves the errors
key in meta objects).
So what about profiles that define behavior for reserved query parameters, such as filter
?
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.
Yeah, this was written with profiles that add data to the document in mind, not those that just use query parameters. Although, on second thought, it's probably a good idea even for a profile like fancy filters to reserve a key (probably somewhere in meta
) so that, if it ever wants to add some metadata to the response (e.g., the size of the unfiltered set, or a deprecation warning or something), it can do so.
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.
We can probably clarify intent here by saying something like:
In order to support the potential to add features over time, profiles SHOULD reserve at least one object-valued member in any portion of the document in which they may realistically expand in the future.
_format/1.1/index.md
Outdated
|
||
A profile **SHOULD** explicitly declare "keywords" for any elements that it | ||
introduces to the document structure. If a profile does not explicitly declare a | ||
keyword for an element, then the name of the element itself is considered to be its |
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 don't think I fully comprehend this, nor its intent. I think a concrete example would help.
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.
That's fair. An example would probably go a long way here.
The point of keywords is to allow any member that's defined by profile to be aliased in a particular implementation. This aliasing might be as simple as ensuring that an API that uses camelCase throughtout does not have to use dasherized-case to comply with a particular profile (e.g. totalCount
vs. total-count
)
_format/1.1/index.md
Outdated
in that document that it does not understand. The only exception to this is profiles | ||
whose support is required using the `profile` query parameter, as described below. | ||
|
||
#### <a href="#profiles-sending" id="profiles-sending" class="headerlink"></a> Sending Profiled Documents |
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 think this section would be better placed after the next one; I think reading is more common than mutating. I first didn't realize that this section was only about mutating.
_format/1.1/index.md
Outdated
|
||
Likewise, clients and servers applying profiles to a JSON API document **MUST** | ||
include a [top-level][top level] [`links` object][links] with a `profile` key, | ||
and that `profile` key **MUST** include a [link] to the URI of each profile |
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.
When no profiles are specified in the request document, but they are specified in the Content-Type
request header or the profile
URL query parameter, should a 400
error response be sent?
IOW: are JSON API documents in request bodies required to match the Content-Type
and/or profile
URL query argument?
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.
On second thought, I don't think this makes sense, because it's already valid to just send
{
"data": { … }
}
Because http://jsonapi.org/format/#document-top-level already says that only one of data
, errors
and meta
is a MUST-have.
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.
When no profiles are specified in the request document, but they are specified in the Content-Type request header or the profile URL query parameter, should a 400 error response be sent?
This is a good point. I think we want to say that, on requests, if the profiles listed in Content-Type
don't match those listed in the document body, the server should return a 400.
On second thought, I don't think this makes sense, because it's already valid to just send
I don't think that prevents us from requiring a 400 in this case, because none of the requests that are valid today will contain profile
in the Content-Type
.
_format/1.1/index.md
Outdated
|
||
For instance, the following document includes a single profile descriptor for a | ||
profile that, let's say, assigns meaning to a `version` keyword that can be | ||
included in any resource's `meta` object. This descriptor provides an alias for |
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.
Once we have a mechanism to explicitly declare a keyword, that also has as a side benefit that that declaration could also state whether the keyword applies to meta
, a query param, or something else still.
FYI: this is being implemented for Drupal at https://www.drupal.org/project/jsonapi/issues/2955020. |
@wimleers Thanks for the review, and sorry for my delay in addressing your comments. I've been swamped. I don't think any of your comments suggest that this PR needs serious changes, which is reassuring. The possible exception is the around the developer experience of requiring long urls in the EDIT: I think the solution here is simple: the server can interpret each query parameter as coming from the profile of its choice by default (though it can't change that choice over time). In other words, it can implicitly interpret Does that approach work for everyone?
I'm not sure I understand this. Can you give an example? |
@ethanresnick I agree that this is the most simple and elegant solution. The critical part here is that the JSON:API resource(s) (i.e. HTTP resource) identified by a request does not change for a given URL. If usage of a profile targets different resource(s), then the same URL must not be used. And we explicitly provide the |
No need to apologize — I know the feeling all too well!
Indeed! :) 👍
I'll let @gabesullice reply to this — it's possible I'm misinterpreting what he wrote.
Agreed! @ethanresnick++ |
Technically, this rule is required to make it legal to add even a new, optional member to the timestamps object.
This commit just pulls the existing guidelines about authoring profiles into a dedicated section, without changing any of the substance. I’m pulling this out because there are a few requirements for authoring profiles that I think are missing, and it was getting a bit messy to have the rules for authoring profiles all mixed in with the rules for using them.
8132c15
to
f8770df
Compare
@ethanresnick Thanks for making those adjustments. I think this is finally ready to squash and merge! 🚀 |
@dgeb AWESOME. I'm going squash this down a bit (though maybe keep more than one commit, as having some distinct history is useful I think) and then will merge. |
Actually, jk, there aren't really neat commit divisions anymore anyway, so squash and merge it is. |
Woot! Thanks for all your work on profiles over the years, @ethanresnick. This one is more yours than mine. |
Thanks Dan! Next up: operations :) |
Exciting! 🎉 Congrats! And thanks :) This means that the Drupal JSON API module — which is currently on a path to inclusion into Drupal core itself — now actually could implement this. How certain are we that there will be no more breaking changes in the spec wrt profiles? I reopened the Drupal.org issue: https://www.drupal.org/project/jsonapi/issues/2955020#comment-12781015. |
I can't forsee any changes, except if implementation experience proves that some part of this design is really unworkable. |
Note: Although this PR has my name attached to it, it represents a lot of hard work by @ethanresnick in particular over the past several years. Ethan, @wycats, and I have closely collaborated to reach a mutually acceptable solution in an attempt to finally push this over the finish line. This may yet be tweaked a little as some last minute adjustments have yet to be reviewed by Ethan and Yehuda. And of course we welcome feedback from the rest of the community.
Profiles are a means to describe additional semantics the JSON API media type, without altering the basic semantics. In other words, a profile describes a fully-compliant, yet more opinionated, implementation of the spec. It is hoped that profiles will unlock new layers of shared conventions.
A profile can essentially turn every MAY or SHOULD in the base spec into a MUST. Profiles might describe how the
filter
orpage
params are used. They might specify that every resource contains aself
link, or that resources should include timestamps or versions as either attributes or inmeta
.Profiles are identified by URI, which ideally should dereference to a document that describes the semantics of the profile.
One or more profiles may be associated with the JSON API media type through the
profile
media type parameter. The application of profiles to a particular document can be specified by clients and servers via theContent-Type
header. The application of one or more profiles to a response can be requested by a client via theAccept
header.In order to require the application of one or more profiles, the profile(s) must be specified with the
profile
query parameter. This allows clients to request a profile or profiles and receive a hard failure if they can't be applied.Any structural elements introduced by a profile can be aliased. This allows for better adaptability and composition of profiles. A new profile descriptor object is introduced which allows for declarations of aliases in a particular document.
Closes #1207
Closes #1195
Closes #762