See #2940342-25: Cacheability metadata on an entity fields' properties is lost and #2940342-30: Cacheability metadata on an entity fields' properties is lost.
Blockers:
- #2940342: Cacheability metadata on an entity fields' properties is lost
- #2945093: Comprehensive JSON API integration test coverage phase 3: test JSON API-specific use cases: related/relationship routes, includes and sparse field sets
(Without those issues landing first, there's a big risk of regressing.)
| Comment | File | Size | Author |
|---|---|---|---|
| #44 | interdiff.txt | 1.73 KB | wim leers |
| #41 | 2948666-41.patch | 26.83 KB | wim leers |
| #41 | interdiff.txt | 1.23 KB | wim leers |
| #38 | 2948666-38.patch | 26.95 KB | wim leers |
| #38 | interdiff.txt | 923 bytes | wim leers |
Comments
Comment #2
wim leersComment #3
wim leersComment #4
wim leersNow only #2940342 is a blocker.
Comment #5
wim leers#2940342: Cacheability metadata on an entity fields' properties is lost landed some time ago, this is no longer blocked!
Comment #6
gabesulliceComment #7
wim leersYoink, taking this one.
Comment #8
wim leers(Worked on this on the flight to DrupalCon Nashville!)
This is basically the goal; the code this issue should remove.
Comment #10
wim leers#2952714: Group module's GroupAccessResult::allowedIfHasGroupPermission(s)() does not include cacheability may end up blocking this.
Comment #11
wim leers#2952714: Group module's GroupAccessResult::allowedIfHasGroupPermission(s)() does not include cacheability was derailed, and is now a
groupissue. #2966384: JSON API's LinkManager does not handle cacheability correctly, just like its origin (HAL's LinkManager) is the successor.And while related, AFAICT it won't block this.
Comment #12
wim leersRebased #8.
Comment #14
wim leersAll the failures in
JsonApiDocumentTopLevelNormalizerTestare due toi.e. the test was relying on the fact that it is okay to call the normalizer directly, but since #2940342: Cacheability metadata on an entity fields' properties is lost, the cacheability is being bubbled as intended, via normalizer value objects. And this patch is removing
JsonApiDocumentTopLevelNormalizer's duplicate/direct bubbling onto the response object's cacheability ('cacheable_metadata' => $response->getCacheableMetadata(),), hence the assertions are failing, even though the bubbling is definitely still happening.All content entity type integration tests are passing just fine. Besides
JsonApiDocumentTopLevelNormalizerTest, only the config entity type integration tests are failing, and they're all failing for the same reason: the tested config entity's cache tag is missing.Unfortunately, the Symfony serializaton system is what gets in the way here: in principle it requires that an
arrayis returned. But we're already not doing that forEntityNormalizer(returnsEntityNormalizerValue),FieldNormalizer(returnsFieldNormalizerValue), and so on. The only place where we are doing this, is in\Drupal\jsonapi\Normalizer\JsonApiDocumentTopLevelNormalizer::normalize(). That first constructs aJsonApiDocumentTopLevelNormalizerValueobject (similar to the aforementioned ones), but then it still converts it to an array. In the process of converting it to an array, the cacheability is lost. That's whyexisted, but this patch wants to remove it.
So, how to proceed?
Well, by making
JsonApiDocumentTopLevelNormalizerbehave like all other normalizers in JSON API and returning a value object, so that the cacheability can be returned! It's up to the caller to convert this to an array prior to encoding.Comment #16
wim leersComment #18
wim leersAhhh, much better! Down to 3 failures!
Those 3 failures are actually 4 errors and 1 failure in
JsonApiDocumentTopLevelNormalizerTest. Specifically,::testNormalize(),::testNormalizeUuid(),::testNormalizeConfig()and::testCacheableMetadata(). All of those failures are happening because that kernel test still is expectingJsonApiDocumentTopLevelNormalizerto return arrays rather than value objects. Easy to fix.(Plus a failure in
EntityToJsonApiTest. But we'll deal with that later. Since that service is going away in the 2.x branch, it's the lowest priority.)Comment #19
wim leersThe remaining failures are 2 errors in
JsonApiDocumentTopLevelNormalizerTest::testNormalize()and a failure in::testNormalizeUuid(). Because includes aren't being rasterized. Because that logic was moved to\Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber::toArray()in #14.But in the case of the top-level JSON API document's normalizer value object, it makes sense for its rasterization to include
includes, since there's no next level to bubble it to! So we can simplify this code, and simultaneously fix things.Comment #22
wim leersNow
JsonApiDocumentTopLevelNormalizerTestis passing! ButJsonApiFunctionalTest::testRead()is failing:First fixing
EntityToJsonApiTest. Single line fix.Comment #24
wim leersThat last failure, in
JsonApiFunctionalTest::testRead()was triggered by the changes in #19's patch.And it actually is related to one of the problems I found earlier. This is what HEAD outputs for
/jsonapi/node/article/2ba2f3ba-6625-4874-8ec8-6e4327055f84/relationships/uidinHEAD:Note how the top-level
"jsonapi"key is missing. We actually have an issue for this: #2949807: Spec Compliance: Error responses are missing the `jsonapi` top-level member!The root cause of this bug is made obvious by this patch:
Apparently the to-be-rasterized value can be either
JsonApiDocumentTopLevelNormalizerValueorRelationshipNormalizerValue. But we'd expect this to always beJsonApiDocumentTopLevelNormalizerValue!And since #19 moves the "rasterization of includes" logic to
\Drupal\jsonapi\Normalizer\Value\JsonApiDocumentTopLevelNormalizerValue::rasterizeValue(), it's now only happening forJsonApiDocumentTopLevelNormalizerValue, and no longer forRelationshipNormalizerValue. (In HEAD, this happens inJsonApiDocumentTopLevelNormalizer, which may be called with aRelationshipNormalizerValueobject.)The failing line is:
… i.e. the includes are missing from the rasterization. This makes sense given the above analysis!
The solution is pretty simple: always require the to-to be normalized/rasterized value to be a
JsonApiDocumentTopLevelNormalizerValuevalue object. Also when accessing the relationship route. That also allowsResourceResponseSubscriberto be much simpler. The one complication is thatRelationshipNormalizerValuealso needs to bubble arelatedlink, but that's easy enough to support inJsonApiDocumentTopLevelNormalizerValue::rasterizeValue().Comment #25
wim leers#24 should make
JsonApiFunctionalTest::testRead()pass again. But now theResourceTestBase::testRelationships()tests will fail (expected[], actualFALSE). Slight tweaking ofJsonApiDocumentTopLevelNormalizerValue::rasterizeValue()'s logic is sufficient. The line in question only really matters for collections, so the change should be fine. Our extensive test coverage will help (dis)prove the change.Comment #26
wim leersCS violations.
Comment #30
wim leersComment #31
wim leersGreen! 🎉
Time for clean-up. Fixed the last CS violation. Updated
ResourceResponseSubscriber's class docblock per the changes above. And most importantly: removed all the render context shenanigans (which includes capturing of out-of-band bubbled cacheability) fromResourceResponseSubscriber, which makes it much, much simpler.Comment #32
wim leersSelf-review:
This could use a comment.
#2962461: JsonApiDocumentTopLevelNormalizer is SerializerAware but doesn't get the serializer injected is doing just that, and is green!
This is effectively doing half of what #2949807: Spec Compliance: Error responses are missing the `jsonapi` top-level member aims to do: it fixes the problem for relationship responses, which means that #2949807 only still has to take care of error responses.
Comment #33
wim leersGreat, #31 passed tests too! There is still one more place where we need to remove render context-based cacheability capturing: JSON API's
RequestHandler. Let's see if tests still pass with that removed. (I already know they won't.)Comment #35
wim leersOne small thing was forgotten in #2966384: JSON API's LinkManager does not handle cacheability correctly, just like its origin (HAL's LinkManager).
Comment #37
wim leersOf course
Shortcuthas to be special. The root cause is\Drupal\link\Plugin\Validation\Constraint\LinkNotExistingInternalConstraintValidator::validate()callingUrl::toString(), which bubbles cacheability. Created #2982060: Un-skip skipped JSON:API tests because LinkNotExistingInternalConstraintValidator is no longer incorrectly bubbling cacheability to fix this.Comment #38
wim leersCleaned up #33 (unused
use, but most importantly: removed the injectedrendererservice).Also addressed #32.1.
Comment #39
wim leersAll green. IMHO this is ready to be committed.
Comment #40
wim leers#2962461: JsonApiDocumentTopLevelNormalizer is SerializerAware but doesn't get the serializer injected landed, now addressing #32.2…
Comment #41
wim leersComment #42
gabesulliceThis is awesome. I have just a couple nits that can be addressed on commit.
😍
I don't really understand this comment.I think this makes sense if yous/only/just/Nit: can these be on single lines?
Nit: Let's use
array_mergehere. Unless you have a specific reason to use+=?+=will not override the self link if it is already set, but I think we always want this to be the value.🤘
Comment #44
wim leers🎉🚀
+=to not overwrite it, since\Drupal\jsonapi\Normalizer\Value\RelationshipNormalizerValue::getLinks()already computes it. There's no point in recomputing it. So, kept this.