Problem/Motivation
According to RFC8288, a link is:
a typed connection between two resources and is comprised of:
- a link context,
- a link relation type (Section 2.1),
- a link target, and
- optionally, target attributes (Section 2.2).
When LinkCollection::merge
merges two Link
objects with the same key and target URI, it does not consider target attributes as a differentiating factor.
An example where this can break link semantics in via the anchor
target attribute. Typically, a "link context" is just the resource on which a link appears, however, a link context can be overridden by the anchor
target attribute. When JSON:API merges links, it only considers the links object key and the target URI to determine if a link is equivalent. In the case that one link defines an anchor
and the other does not, the semantics of the link without the anchor
will be broken. Consider the following example (appearing on a resource /bar
):
1. {"href": "/foo", "anchor": "/foo/alias", "rel": ["canonical"]}
2. {"href": "/foo", "rel": ["related"]}
These JSON objects are simplified. Real JSON:API link objects look different.
would be merged into:
3. {"href": "/foo", "rel": ["related", "canonical"], "anchor": "/foo/alias"}
Another example where this can be an issue is when two links share a common URI but different link relation types and target attributes:
1. {"href": "/foo", "rel": ["comments"], "title": "Comments on Foo"}
2. {"href": "/foo", "rel": ["add-comment"], "title": "Post a comment about Foo"}
would be merged into:
3. {"href": "/foo", "rel": ["comments", "add-comment"], "title": ["Comments on Foo", "Post a comment about Foo"]}
After that merge, it's impossible to know which of the two links represented by the merged object has which title
. The semantics of the link object are pretty much nonsensical.
For background, the reason that Drupal\jsonapi\JsonApiResource\Link
support more than one link relation type and merging of this kind is because RFC 8288 permits a Link
header to have more than one link relationship type and, apparently, I misread the RFC (mea culpa). In the following paragraph, I saw the first sentence, but didn't internalize the second sentence:
The
rel
parameter can, however, contain multiple link relation types. When this occurs, it establishes multiple links that share the same context, target, and target attributes. — Section 3.3 (emphasis added)
Proposed resolution
Deprecate passing an array of link relation types toLink::__construct
in favor of a single stringDeprecateLink::getLinkRelationTypes
(plural) in favor ofLink::getLinkRelationType
UpdateLink::compare
to only treat equivalent links as equal (it currently ignores target attributes)UpdateLink::merge
to only merge cacheability, since links with different relation types or attributes can no longer be merged
User interface changes
None.
API changes
Link::__construct
will have a deprecated argument type. Note: Drupal\jsonapi\JsonApiResource\Link
is an @internal
value object and no JSON:API code actually uses multiple link relation types or target attributes.
It's also important to note that there is currently no public API for adding custom links to JSON:API responses.
Data model changes
None.
Release notes snippet
None.