Problem/Motivation
See #2860350: Document why JSON API only supports @DataType-level normalizers and particularly #2860350-33: Document why JSON API only supports @DataType-level normalizers and #2860350-36: Document why JSON API only supports @DataType-level normalizers.
Quoting #2860350: Document why JSON API only supports @DataType-level normalizers:
JSON API does not inherit Drupal 8.3's improved normalization of primitive/scalar fields (#2751325: All serialized values are strings, should be integers/booleans when appropriate)
But actually, when I step through it with a debugger, it looks like \Drupal\serialization\Normalizer\PrimitiveDataNormalizer is being used, and \Drupal\jsonapi\Normalizer\ScalarNormalizer is not!
Proposed resolution
Remove \Drupal\jsonapi\Normalizer\ScalarNormalizer.
Remaining tasks
- Patch.
- Test coverage.
- Review.
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #36 | 2929935-16.patch | 4.61 KB | wim leers |
| #32 | 2929935--FUTURE--remove_bc_layers-do-not-test.patch | 4.66 KB | wim leers |
| #30 | 2929935-29.patch | 4.88 KB | wim leers |
Comments
Comment #2
wim leersComment #4
e0ipso👏🏽 great research! Thanks @Wim Leers.
I'm assuming we need to remove the Unit tests for this class.
Comment #5
wim leersThere's no unit test for this class, but apparently there's some entities that do use
ScalarNormalizer. I couldn't get things to fail with article nodes or taxonomy terms though.Comment #6
wim leersIt looks like at least all config entity normalizations are using this, this fatal error shows up multiple times in the failed test output:
Comment #7
gabesulliceThe problem @Wim Leers found is nearly identical to #2903261-2: Error serializing field added by contrib module.
ScalarNormalizer is wrapping everything in FieldNormalizerValue, which is later expected in ConfigEntityNormalizer.
When the ScalarNormalizer is removed and PrimitiveDataNormalizer kicks in, the implicit dependency on FieldNormalizerValue breaks.
My hunch is that ConfigEntityNormalizer is not the only place that this will be the case, given the ubiquity of ScalarNormalizer's use (basically everything will pass through it at the lowest level I think?).
Comment #8
wim leersThanks for investigating that!
I do wonder if
ConfigEntityNormalizerwill end up being the only place affected by this. Because for content entities, everything automatically gets wrapped in aFieldNormalizerValue(see\Drupal\jsonapi\Normalizer\FieldNormalizer::normalize()+\Drupal\jsonapi\Normalizer\FieldNormalizer::normalizeFieldItems()).The root cause of this bug seems to be that
\Drupal\jsonapi\Normalizer\ConfigEntityNormalizer::serializeField()blindly assumes that either it'll get back an array (in which case it does this wrapping too, just like for content entities above), or it already is aFieldNormalizerValue. That should have an assertion at the very least.Interestingly,
ConfigEntityNormalizer::serializeField()does do something like this for array values, since #2795109: [BUGFIX] Support configuration entities with array values.More interestingly,
ConfigEntityNormalizer::serializeField()is calling$this->serializer->normalize()on every field. Despite it being a fact that it always contains only scalar data, thanks to\Drupal\jsonapi\Normalizer\ConfigEntityNormalizer::getFields(). That's how #2733097: [FEATURE] Add GET support for configuration entities introduced this normalizer in the first place.Which means that AFAICT we can fix this by hugely simplifying
ConfigEntityNormalizer. It needs to do only two things:$this->serializer->normalize()is not an array: if it is not, turn it into an array, becauseFieldItemNormalizerValue()requires an array to be passed$outputinFieldItemNormalizerValue()andFieldNormalizerValue()This passes
JsonApiFunctionalTest.Interestingly, this also affects the set of cache tags that are asserted in
JsonApiDocumentTopLevelNormalizerTest. I've not yet been able to determine the root cause for that. That'll need further analysis.Comment #9
wim leersAnd d.o ate my files in #8. 😩😵
Comment #11
wim leersAnd actually, we should be able to remove that
$this->serializer->normalize()call altogether!Comment #13
wim leersOh, interesting! #9 is failing because of the changes that I mentioned at the bottom of #8 that I did not yet understand:
… so when the tests run on d.o, these changes are not actually necessary. This is … strange. Reverting those changes for now.
Comment #14
gabesulliceI think we can remove this altogether now.
@Wim Leers, I'm not clear on the caching issue, is that something that still needs to be fixed?
Comment #15
wim leers👌 Done!
If you apply the patch locally and run
JsonApiDocumentTopLevelNormalizerTest, does it pass?Comment #16
wim leersOh, and it was #2751325: All serialized values are strings, should be integers/booleans when appropriate that added
\Drupal\serialization\Normalizer\PrimitiveDataNormalizer/serializer.normalizer.primitive_data, which was in Drupal 8.3.0.So this change means that the JSON API module now requires Drupal 8.3.0 at minimum. Which is fine, because 8.4.x is the only supported version anyway, and 8.2.x is absolutely unsupported.
Comment #17
gabesulliceYep.
Comment #18
pcambraI am not sure that unsupported versions grant for breaking BC, is there any reason to remove this instead of just marking it deprecated as core does?
Drupal 8, specially in the API area is very much moving target with still big changes in the works such as #2543726: Make $term->parent behave like any other entity reference field, to fix REST and Migrate support and de-customize its Views integration that might force some sites to lag behind.
Comment #19
wim leersThen it's a problem on my machine. Interesting. Then the cacheability portion is done too.
First: There is zero functional change. Normalizers are an implementation detail, not an API.
Second: Drupal 8.2 is definitely unsupported. In fact, 8.3 is also unsupported, because 8.4 is out. See https://www.drupal.org/core/release-cycle-overview.
Third: we're not removing an API, we're removing an implementation detail that never was an API. Core does this too.
This is why issues like #2929932: Work around core's ill-designed @FieldType-level TimestampItemNormalizer normalization until #2926508 lands exist: to port fixes of the next Drupal 8 minor to the current JSON API release, so that sites using JSON API can benefit from it now rather than in the future.
Comment #20
e0ipsoIf you're running D8.2 this reverts the use of scalar normalizers. Your integers are back to
"42"instead of42, unless I'm mistaken, since$output = $this->serializer->normalize($field, $format, $context);was dropped and there is noPrimitiveDataNormalizerthat kicks in its place for 8.2Would that be a functional change for 8.2?
Comment #21
wim leersMoving back to , I want @e0ipso to RTBC this :)
Yes.
Like I wrote in #16:
Comment #22
e0ipsoWhen something gets deprecated in core, we keep it around until D9. Even though something was deprecated in D8.1, and that's unsupported and everyone is supposed to be in D8.4+. I think that's because we acknowledge that people can either get stuck in older versions of Drupal and/or their custom code may be.
In the context of someone being in D8.2 (like Pedro seems to be), merging this patch in 1.x could mean breaking their site.
I'm RTBCing this patch, but I will create
jsonapi-8.x-2.xbecause of it. I think that's the best course of action. I will not tag a release just yet so we can identify other breaking changes that we want to introduce and craft some upgrade notes.How does that sound?
Comment #23
e0ipsoComment #24
wim leersI think you're confusing functionality backwards compatibility (in this case: the HTTP responses generated by the module are the functionality) with API backwards compatibility (PHP APIs).
Drupal 8 will keep API backwards compatibility until 9.0.0. It does refactor existing code away to use new APIs, as long as it keeps functionality backwards compatibility.
This issue is keeping backwards compatibility (both API and functionality). It only requires you to be on the up-to-date on Drupal core minor to keep functionality BC. (Necessary to be secure.) This never even was an API, so that BC can't be broken.
Finally: if you're not going to update Drupal core, then why would you update JSON API?
Before you do that: are you going to do this for every next minor too? Because this will occur again in the future.
Comment #25
wim leersJust discussed this with @e0ipso. He tended to agree with #19 in the end, but I proposed something different still: keep
ScalarNormalizer, but only add it whenPrimitiveDataNormalizeris not present. That would mean 8.3, 8.4, 8.5 etc would use core'sPrimitiveDataNormalizer, and 8.2 would use JSON API'sScalarNormalizer.Patch soon.
Comment #26
wim leersThis should work on 8.2, 8.3, 8.4 and 8.5. But
\Drupal\Tests\jsonapi\Unit\Normalizer\ConfigEntityNormalizerTest::testNormalize()should fail on 8.2.Comment #27
wim leersEhm … so I wanted to queue an 8.2 test for #26, but I can't, because d.o only allows testing against the current minor (8.4), the previous minor (8.3) and the next minor (8.5).
Comment #28
wim leersThis should make
\Drupal\Tests\jsonapi\Unit\Normalizer\ConfigEntityNormalizerTest::testNormalize()pass on 8.2. (I didn't test it, because I don't want to change my entire development environment.)Comment #29
wim leersFinally, these are the deprecation indications that are necessary.
Comment #30
wim leersCorrect patch for #29.
Comment #32
wim leersSo now it's up to @e0ipso to decide.
Comment #33
wim leersSo … the problem I pointed out in #13… I figured out why I was the only one able to reproduce this locally.
Because that only fails against 8.5, and both DrupalCI and @gabesullice have been testing with 8.4. And JSON API's tests haven't been running against 8.5: #2931831: Tests not running against Drupal 8.5 anymore.
Created #2931844: Make JsonApiDocumentTopLevelNormalizerTest pass on Drupal 8.5 to fix that.
Comment #34
e0ipsoThis is pretty nice. However I think that the best option is to remove support for 8.2.
Once the blocker is resolved (#2931844: Make JsonApiDocumentTopLevelNormalizerTest pass on Drupal 8.5) let's merge #26 (no 8.2 from now on).
Thanks for humoring me and providing options 💆🏽♂️
Comment #35
wim leersOf course! I do want you to feel comfortable. And the pace of development and feedback is 10 times faster here in the JSON API contrib module than in core, so then I definitely don't mind creating alternative solution patches so you can pick the one you feel most comfortable with :)
It's so great to collaborate with you, and to make decisions based based on carefully evaluated options! ❤️👍
Comment #36
wim leers#2931844: Make JsonApiDocumentTopLevelNormalizerTest pass on Drupal 8.5 landed, so this can move forward now.
@e0ipso made a decision in #34, but I still want to leave it up to him to commit this, in case he changes his mind.
Reuploaded the patch from #16, testing against both 8.4 and 8.5. Should come back green now.
Comment #38
e0ipsoThanks all!
Comment #39
wim leersYay!