At the moment JSON:API is not able to normalize a list of revisions of the same entity because of normalizations' cache. If you pass 10 revisions the normalized structure for all of them will be the same since the first result is cached and used because of matching cache tags, which are resource name and entity ID.
I understand that this functionality is not offered by JSON:API out of the box and to achieve this I wrote a custom code that relies on PHP API (the jsonapi.entity_resource service in particular) even despite a warning that developers shouldn't do that.
Nevertheless, the solution to overcome this problem is simple and seems so logical so I don't think it should stay overboard.
Proposed resolution
Include a revision ID to the cache tags.
API changes
Every 403 error object (omission) receives two new members in the links.via.meta node: resourceId and resourceVersion. The existing value at links.via.href may include the resourceVersion GET query parameter if a revision ID is known (entity is revisionable and has a revision).
Example:
{
"links": {
"via": {
"href": "https://example.com/node/12?resourceVersion=id%3A12",
"meta": {
"resourceId": "entity_uuid",
"resourceVersion": "12"
}
},
}
}
The resourceId is always present and is a UUID of omitted entity. The resourceVersion is always present and can be null in the case when entity has no revision (Drupal\Core\Entity\RevisionableInterface::getRevisionId() returns null) or non-revisilable (the entity class does not implement the Drupal\Core\Entity\RevisionableInterface). A result of getRevisionId() otherwise.
| Comment | File | Size | Author |
|---|---|---|---|
| #56 | 3149854-jsonapi-revision-id-56.patch | 42.05 KB | zarpele |
| #55 | 3149854-jsonapi-revision-id-55.patch | 45.25 KB | stefan.butura |
Issue fork drupal-3149854
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
br0kenComment #3
br0kenComment #5
br0kenComment #6
br0kenThe test to show the problem.
Comment #7
br0kenThe fix.
Comment #8
br0kenProbably we don't need to have the `toCache` array associative.
Comment #11
br0kenSetting back to NR.
Comment #12
wim leersWoah, good catch!
Why is removing the key okay?Because the key was not used anyway!
✅
Woah! First time I'm seeing this 🤓
👍 This makes total sense! This is the root of the bug described by the issue title.
🤔 This is over my head right now 😅 Why are we appending a count to a hash salt?
Comment #13
br0ken#12.4 Imagine a user that doesn't have access to the entity's revisions and opens a page with 10 of them. The `omitted` will contain only one (latest) section instead of all 10. This happens because all `href` for all revisions of a single entity are the same which leads to producing the same hash hence the same array key the value for which will always be overwritten. Actually I feel like this needs a test too.
Comment #14
br0kenComment #15
br0kenRemove global NS for SPL functions as Drupal core doesn't use it.
Comment #16
br0kenHere is a more elegant solution for storing all omissions which also provides a version ID in
meta/version.Comment #18
br0kenAnd of course the more elegant the solution is the more work it requires.
Comment #21
br0kenRe-roll for 9.3.x. Tests won't pass as I don't know an easy way to overcome the generalized
assertResourceErrorResponsemethod and how to pass revision IDs there from so many places. The tests are too cumbersome to work with. From the operability perspective, the patch fixes the issue. Hopefully, someone will have advice or idea about the tests.Comment #22
br0kenI accidentally named the patch incorrectly plus left there an unused import.
Comment #23
br0kenI should have been more attentive.
Comment #25
br0kenSetting back to NR to attract attention.
Comment #26
bbralaThanks @BR0keN, i think this is a smart addition and will make life easier later on.
I've gone through the code and there is a few small things that i think would need work. More importantly, there is quite a few test failures that seem to point at some empty array values. We do need to get to the bottom of that.
Removing the key here feels a bit scary, but i can't seem to find a way this would create issues.
I understand this seems nicer, but i think it is wise to move this change to a folow up issue to minimize the change surface of this patch
Another reason for #3252424: Follow-up: ResourceObjectNormalizationCacher should get tags from ResourceObject instead of building them i think.
Shouldn't we keep this outside the loop? Don't think there is a real reason to move it inline and calculate it in the loop instead of outside.
Comment #27
br0kenHey Björn, thanks for your attention to this.
The problem with tests is due to the generalized method used across the entire test suite, which is not flexible enough to accommodate it for the new changes. I'm really disappointed that I need to rewrite lots of tests code to make them pass with the changes this patch introduces.
If you wish, we could chat more about this in Slack.
Comment #28
bbralaWegarding the tests, we are quite explicit in what we expect as output, so if we change the output, the tests will fail. Not sure how we can go around that. I remember #3036593: Add 'drupal_internal__target_id' to JSON:API representation of entity reference fields, because that can't be retrieved from the target resource for target entity types without corresponding resources, was a little bit of effort as seen in the commit.
I've also pinged you onm slack
Comment #29
bbralaWe had some discussion on slack regarding this issue:
Comment #30
br0kenAdded proposed changes. This should help
testCollectiontests howevertestGetIndividualandtestPatchIndividualwill still fail.Comment #31
bbralaLets have a look after this run. I think this will probably only be updating getExpectedDocument for a few classes.
Comment #33
br0kenSummary of 65 fails:
It's really hard to edit these tests. Offtopic: lines longer than 140 chars are hard to read. I also often stumble upon 200+ chars per line.
Comment #35
br0kenA 5-minutes fix and already more than 10 hours to adjust messy tests.
Comment #36
bbralaI'm going to try some things in a seperate branch to see if i can clean up how tests is configured.
Comment #38
bbralaOpening merge request of my changes to have tests run without changing the branch by @BR0ken
Comment #41
bbralaOk, the changes I made to how the tests are run are a lot cleaner I think. The following is needed still:
/jsonapi/user--user/relations/revision_iddoesn't provide that information. Is that correct? It really does need to be clear when it is added.After that i'll have a look if the tests are correct in respect to the proposed change, but for that I really want to know explicitly what is added.
Also; added 'Needs change record' since this change will need a change record.
Comment #42
bbralaAnother thing i thought if, why should the revisionId be included in the meta if it is NULL, wouldn't this mean, there is no revision and shouldn't it then just be omitted?
Comment #43
br0kenTwo new members are added to the omission error object only (see updated IS for details).
Shall we now continue in the MR you've opened?
Comment #44
br0kenComment #45
bbralaPersonally I find mr's easier to work with :)
Comment #46
br0kenHmm...
I found two occurrences of the
The current user is not allowed to view this relationship.. In both cases, theEntityAccessDeniedHttpExceptionis constructed and I suspect it's not getting normalized and that's why necessary meta members are missing.Edit: ^ when it passes through
ResourceTestBase::getExpectedCollectionResponse.Comment #47
br0ken🤞
Comment #48
br0kenHaving #3199696: Add language support to ResourceObject merged to 9.4.x and not backported to 9.3.x will cause a conflict if this gets committed to 9.3.x. Two options:
Comment #49
br0kenComment #54
stefan.butura commentedSee next comment
Comment #55
stefan.butura commentedAttached an attempt at creating a patch from MR 2417 that is compatible with Drupal 10.3.x
Comment #56
zarpele commentedAttached a patch from MR 2417 that is compatible with Drupal 10.4.x