Postponed
Project:
JSON:API
Version:
8.x-2.x-dev
Component:
Documentation
Priority:
Major
Category:
Task
Assigned:
Unassigned
Reporter:
Created:
31 Jan 2019 at 23:52 UTC
Updated:
5 Apr 2019 at 14:28 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
effulgentsia commentedActually, I'd rather do it this way. What do you think?
Comment #3
effulgentsia commentedComment #4
effulgentsia commentedTagging for issue summary update, but I'd like to get initial feedback on the patch first.
Comment #6
effulgentsia commentedComment #8
effulgentsia commentedComment #9
gabesulliceNice, thanks @effulgentsia! This is essentially the direction I saw us going. You beat me to making the issue and writing the patch :) It's nice to see it validated. So ofc, I have no objection to the general approach.
Just some mid-level stuff:
What does extension buy us?
This is nit-picky, but I feel a little weird about this name (I'm sorry, I'm sure you struggled to come up with one because it's hard! Cache invalidations and naming things, right? Double whammy).
My complaint is that "normalization" is about taking objects and making them not objects.
So, what about
toCacheableNormalization($object, $format, $context)?I think a
\LogicExceptioninstead of a "BC layer" makes sense here since our normalizers are all internal.Instead of overriding this and excluding non-compliant normalizers, I wonder if it would be better to shim them. I have some pseudo-code in #3028080-6: Add CacheableNormalization for Normalizer to return Normalized value and Cacheablity that shows what I'm thinking about. However, since this issue proposes a new interface, I think it could be further simplified.
Comment #10
effulgentsia commentedThanks for the review. This addresses all of it.
It's not about excluding non-compliant normalizers. It's about excluding denormalizers that aren't also normalizers. And we have to copy the function because it's private in the base class.
Comment #11
effulgentsia commentedNote that as a consequence of #9.3, we're now losing this test, since normalize() and getCacheableNormalization() are now mutually exclusive rather than fungible. Is that ok or is there a reason we need to retain support for the array-of-fields case?
Comment #13
effulgentsia commentedJsonApiDocumentTopLevelNormalizerTestcallsserialize()(instead of the module's runtime behavior of getting the cacheable normalization, caching it, and then encoding it, all in separate steps). Since being able to call serialize() is a pretty foundational expectation of a serializer, this makes that work by undoing #9.3, but only for the top-level normalizer.Comment #14
wim leers👌Excellent comment!
You used "get" instead of @gabesullice's suggested "to". Both work for me.
👍Strong +1, this is also the biggest concern I had, and @gabesullice already pointed it out :)
🤔 When does this exception occur? Why do we have this new edge case?
🤔This is now identical to the inherited function, so we can delete this completely. Also, it doesn't match the docblock anymore. I also don't understand the justification for this in #10.
Drupal\Tests\jsonapi\Kernel\Normalizer\JsonApiDocumentTopLevelNormalizerTest::testNormalizeException()though. Why not just update the test to not use::serialize()butgetCacheableNormalization()?Comment #15
effulgentsia commentedI was confused about how this even worked in HEAD to begin with, and then found the sneaky code where
\Drupal\jsonapi\Serializer\Serializer::normalize()delegates data for which there's no supported normalizer to the parent class (but still the same json:api serializer object), allowing re-entry back into the json:api normalizers for nested data. I opened #3030853: [PP-1] Do not auto-traverse arrays/traversibles within the JSON:API serializer to either remove support for this entirely, or to handle it better (e.g., by performing proper cacheable normalization aggregation for these cases).Comment #16
gabesulliceComment #17
effulgentsia commentedThanks for #3022583: [META] Normalization System: clean up/speed up/provide schema! Great meta issue! Reparenting this one to #2843147: Add JSON:API to core as a stable module, because I'd love for this one to land before committing JSON:API to core, if possible. I think the rest of #3022583: [META] Normalization System: clean up/speed up/provide schema can happen after that.
Comment #18
e0ipsoThanks for calling this out. I'm starting to believe that before we get this into core we should formalize these implicit PHP APIs.
Comment #19
gabesulliceThis interdiff tries to preserve my goal for the Serializer to be a "drop-in" replacement for the Symfony serializer.
Preserving that as much as possible means that if our normalizers are ever to be reintegrated with the core serialization system, we could just delete our Serializer. I still hope that will one day happen.
Ofc, the
getCacheableNormalizationmethod will make our normalizers forever "different". However, there's a core issue to add this concept to the serialization module. Once/If that lands, then we'd be just as close to reintegrating as we were before.Now that our serializer has the
getCacheableNormalizationmethod that uses the fallback serializer, I was able to get rid of some complexity in a couple normalizers too. I think those changes are fairly straightforward and can be included in this patch.Comment #20
wim leers+1
Indeed.
Comment #22
gabesulliceEDIT: The 6.44KB interdiff.txt is the right one (ignore the other).
Trying to normalize a traversable before giving the fallback normalizer a chance to normalize it means that there was a regression of the fix here: #2958166: Traversable Object with custom normalizer can't be right normalized
This can be simplified by using CacheableNormalization::aggregate().
Comment #23
wim leers🤔 Nice, all this complexity is now moved out of a the two field-level normalizers and into a single place! 🎉
But … this comes with some a kind of downside too: this now transparently allows any normalizer to descend into "core normalizers". That's purely theoretical though, since we don't allow any additional normalizers to be defined. But it is less clear/explicit which normalizers allow leaving the JSON:API normalizers and "reach into core normalizers": the
@FieldType-level normalizers of JSON:API do this, to get the@DataType-level normalizers.🤔 This was done in #13. I don't think we want this. It feigns compatibility with
serialize(). But … nothing outside thejsonapimodule will end up calling this. And the JSON:API module only normalizes this object in\Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber::renderResponseBody(), and in there it does the normalization and encodings steps separately.In other words: #13 did this just to satisfy
\Drupal\Tests\jsonapi\Kernel\Normalizer\JsonApiDocumentTopLevelNormalizerTest::testNormalizeException().Worse: the cacheability stuff would simply be ignored, because the JSON:API module is the only caller and it does NOT respect
SERIALIZATION_CONTEXT_CACHEABILITYfor its own normalizers, only for@DataType-level normalizers.Finally, as of #19, JSON:API's
Serializerservice already takes care of this anyway (the code quoted in the previous point).So: simply deleted this! ✅
🤔
JsonApiExtrasFunctionalTestfails with this patch applied, because of itsSerializerDecoratorimplementingSerializerInterfaceand this patch changing the typehint to not requireSerializerInterfacebut\Drupal\jsonapi\Serializer\Serializer. Worse, since this patch is also marking that classfinal, there is no work-around for JSON:API Extras possible.I think we want to revert this change.
Comment #24
gabesullice#23.3
+1. I think that may have snuck in from my playing with #3007431: Make more things `final`.
Comment #25
effulgentsia commentedThat's partially a pre-condition of HEAD though. Since the JSON:API serializer forwards all normalize() calls for which there isn't a JSON:API normalizer to the core serializer. I suppose the protection we have in HEAD (and lost in this patch) from unwanted forwards is the assertions in the JSON:API normalizers that what it gets back from a normalize() call of a child data object is an instance of CacheableNormalization. But that requires each such caller to have that assertion.
How about #3032259: Remove the JSON:API serializer's fallback behavior and inject the core serializer in the field item normalizer as a way to both clarify and enforce what we want better?
Comment #26
effulgentsia commentedI'd like to postpone this on #3032259: Remove the JSON:API serializer's fallback behavior and inject the core serializer in the field item normalizer, which itself is postponed on some refactoring of JSON:API Extras. I think it's worth postponing this on that because the purpose of this issue is to establish clearer encapsulation, which we can't do as effectively while Extras is breaking that encapsulation per #3032259-4: Remove the JSON:API serializer's fallback behavior and inject the core serializer in the field item normalizer.
Comment #27
effulgentsia commentedComment #28
wim leers#3036904: Decorate JSON:API's normalizers and fake its namespace so JSON:API can eliminate its Serializer's fallback behavior. landed, which unblocked #3032259: Remove the JSON:API serializer's fallback behavior and inject the core serializer in the field item normalizer, which means this is now down to one blocker.