Many parts of the JSON:API codebase require the resource type of a route and/or entity that is currently being processed. Whether it is to alias a field, validate a filter path, test if a field is disabled, or check if certain features are enabled. This means that the resource type repository is loaded and called frequently throughout the codebase. In some cases, this can be a performance bottleneck (#3014232-27: [regression] ResourceTypeRepository is significantly slower in JSON:API 2, becomes noticeable when handling hundreds of interlinked resources), but also a code smell.
This issue proposes to wrap entity objects in a value object that contains the associated resource type for that entity.
Some benefits:
- This introduces
ResourceIdentifier(Interface|Trait), I think this will lead to further simplifications in the future. - We landed #2995960: Add a Link and LinkCollection class to support RFC8288 web linking., which makes Links value object that we can pass into
JsonApiDocumentTopLevel. With this, we can push pass links into ResourceObjects prior to normalization too. That will standardize/validate the LinkCollection pattern. - This will result in fewer usages of the deprecated LinkManager.
- This will simplify the
IncludeResolver - Eliminate the need for a global-like
$context['resource_type']in the normalization system - The JSON:API normalizer will no longer need to provide a normalizer for entity objects and therefore can't conflict with existing entity normalizers. If we have value objects in our namespace for everything above the data type level, we can begin to ask ourselves, why do we still need
jsonapi.serializer_do_not_use_removal_imminent(especially after #3022584: Consolidate and simplify NormalizerValue objects: introduce CacheableNormalization)?
| Comment | File | Size | Author |
|---|---|---|---|
| #94 | 3015438-94.patch | 110.62 KB | wim leers |
| #94 | interdiff.txt | 667 bytes | wim leers |
| #92 | 3015438-92.patch | 110.56 KB | wim leers |
| #92 | interdiff.txt | 3.03 KB | wim leers |
| #89 | 3015438-89.patch | 110.47 KB | gabesullice |
Comments
Comment #2
gabesulliceHere is my first iteration on this concept.
Comment #3
gabesulliceEntity level normalizers now only handle deserialization.
Normalization is handled by the
ResourceObjectnormalizer.The resource type repository is no longer needed here.
This needs to be updated to accept a resource object.
This is simplified because
ResourceObjectimplementsResourceIdentifierInterface, like all other objects in anEntityCollection.@e0ipso, I think this begins to accomplish some of the things in #3014277: ResourceTypes should know about their fields. I'm interested how they could play together.
This would eliminate between 5 and 11 seconds in #3014232-27: [regression] ResourceTypeRepository is significantly slower in JSON:API 2, becomes noticeable when handling hundreds of interlinked resources according to the linked profile. That is not reason enough to apply this patch, there are obviously much smaller static cache tricks we could employ. I do think it illustrates the point of this issue though: using a global object to store information that should be directly associated with the entity being processed has negative consequences.
Comment #4
gabesulliceThis is obviously still a WIP.
Comment #5
wim leersI was going to ask how this would interact with @e0ipso's proposal in #3014277: ResourceTypes should know about their fields, but I see you're already thinking about that in #3.6. Great!
#3.7: "between 5 and 11 seconds" is sort of meaningless, we want to know the relative speedup. And I'm not sure that's actually true; it'd only be true if we don't commit the static caching patch at #3014232: [regression] ResourceTypeRepository is significantly slower in JSON:API 2, becomes noticeable when handling hundreds of interlinked resources. With that applied, the cost is only 423 ms, and getting that to zero will not be possible. Additionally, that profile includes JSON API Extras, which inherently increases wall time since it needs to load and inspect its config too.
However, that whole static cache/memory cache business in
ResourceTypeRepositoryhas always been a pain. So I most definitely agree with .I'm interested in exploring this further, but only post-2.0. Since all of this is an internal API anyway, we can do that.
I definitely like this, not only because of the simplifications it brings (some of which you described in #3), but also because we then have a value object that clearly maps 1:1 to the JSON:API spec. I suspect this will make the codebase easier to maintain and grok.
Comment #6
gabesulliceRerolled and fixed the CS violations.
Yep, which is why I said: "That is not reason enough to apply this patch, there are obviously much smaller static cache tricks we could employ."
And I think if we had applied this before trying a cache, then the static cache would have looked much less impressive at that point as well. My point was to illustrate that the bottleneck could be solved with a cache OR this. I think adding a cache was obviously the faster/less disruptive solution and I'm glad we did it, however it really only addressed the symptom, not the cause.
Comment #7
wim leersSorry for misunderstanding!
That makes sense.
I wholeheartedly agree!
Comment #8
gabesulliceReroll.
Comment #9
gabesulliceFix the most common failure, a leftover missing variable.
Comment #10
gabesulliceThis should fix all the
field_nonexistanterrors.Comment #11
gabesulliceI'm hoping this turns this patch green! 🤞
Comment #12
gabesullice#11 was close! But this should do it :)
Comment #13
gabesulliceNow, we only use entity normalizers for denormalization. Normalization is applied to
ResourceObjectsinstead of entities.This is a small step in the direction of getting rid of
jsonapi_normalizer_do_not_use_removal_imminentbecause it'sResourceObjects are a value object in our own namespace. We don't need to worry about conflicts with REST normalizers.This change just gets rid of the two similar loops.
The only material change is the introduction of
array_filter($resource_type->hasField(...)).I would have liked to inject this, but this is a static method. The revisions patch extract this method into a service, so this will soon go away.
This should be
ResourceIdentifierInterface.Remove this
@todoor make a followup issue for it.Now all the objects in an
EntityCollectionimplementResourceIdentifierInterface, which leads to some simplifications elsewhere and will hopefully lead to more in the future.Here is one such simplification. No special exception is needed for entities.
The second method can be removed.
This should be moved to
ResourceIdentifiers/JSON API/JSON:API
ResourceIdentifier::__construct()should be able to take aResourceTypeobject or string as its first argument. That would eliminate some requests to the resource type repository.All this logic is simply moved over from the now removed entity-level
normalizemethods.s/JSON API/JSON:API
Another place we can eliminate some requests to the repository.
This use of the repository can go away too.Actually,
LabelOnlyEntityNormalizercango away altogether if
LabelOnlyEntityjust extendsResourceObjectand overridesextractFields!More repository removal.
Comment #14
gabesulliceAll my self review things in #12 have been addressed.
I'm including two patches:
the other "smaller" patch is generated withIt didn't.git diff -M2%; I'm hoping this will make it easier to review.Comment #15
gabesulliceJust ignore the "smaller" patch in #14, it's not easier to review.
Comment #18
gabesulliceSneaky little label callbackses...
Comment #20
gabesulliceThe approach in #18 didn't really work for config entities. I found this simpler way.
Comment #21
gabesulliceAnd the most fun interdiff of them all... I can remove
EntityNormalizer&ContentEntityNormalizernow for a grand total of 297 deletions!Comment #22
wim leers🎶 Music to my ears! Especially since people have been making remarks about that on the core issue
👍
😀
🎉
Actual patch review:
This means we're now normalizing all "resource objects" in JSON:API terminology in exactly the same way.
Denormalization is different out of necessity.
Makes me curious about the rest of the patch :)
I think this simplification could already happen in a separate minor issue.
Let's keep it here for now. If there's many more of these simplifications, lets' spin it out into a separate issue.
Übernit: this comment needs to be updated.
If we're changing the parameters, shouldn't this become
LabelOnlyResourceObject?The old line was correct, this only needs to change because
$entityis no longer anEntityInterfaceinstance. I think it may be less confusing to just rename it to$resource_object… but I'm guessing you didn't do that to minimize change?Why can we remove this TODO?
Not yet renamed to
ResourceObjectCollectionto minimize change probably.(Could happen in a separate issue.)
😍
😍
Comment is wrong.
Nit: what about "decorated"?
Some doubts about exposing a Drupal-specific data structure when this object is all about decorating everything in JSON:API terminology. But I can't think of a better alternative.
👌
😍
I'd even throw a
\LogicExceptionI think.👍
I don't think it makes sense to rename
$entity_listif we're not renaming so many other things, and especially when there's the closely associated/in sync$entity_list_metadata.This is why we're suddenly using
$contextfor crucial things, right?If we're to do this, then let's at least immediately
unset()it after it's fulfilled its purpose. We want to prevent unintentional dependencies on this context.That is not a regression compared to HEAD though. Optimizing this deeply, based on the requested sparse fieldset is not only going to be tricky and perhaps pretty complex, it would also conflict with #2819335-18: Resource (entity) normalization should use partial caching. So I think this TODO can be removed!Ah, I see, I mis understood it. Why move this?
🤘
Nice!
Docs nit.
🤘 That means this is now an immutable value object, great!
Looks like this actually can also be done already in HEAD.
🤘
Nit: inheritdoc.
I believe that
$this->contextwas made obsolete here? :)Comment #23
wim leersI forgot two things:
This will break JSON:API Extras.
Comment #24
gabesullice1. 👍
2. Agreed.
3. Done.
4. Yes, I left it to keep the patch size down. But now that you've reviewed and a generally positive about it, I'll make the change.
5. You're correct. As in #4, I'll make that change now.
6. It's a leftover, #2956084: Impossible to raise an error when an `include` is requested for an inaccessible relationship field. is Closed (fixed).
7. Why don't we keep it in this issue but make it the last thing we do, so that you can just review a final interdiff?
8/9. :D
10. Fixed.
11. I went with "The entity to be represented by this resource object." Decoration adds behavior, a
ResourceObjectactually strips away most behaviors.12. I too sniffed the air because this was a little fishy, but technically "a resource object’s
attributesand itsrelationshipsare collectively called its 'fields'". Good enough for now I think.13/14. :D
15. Done.
16. :)
17. I renamed this to
$relationship_object_metadatato indicate where in the response document that information will end up.18. Done.
19. Because in its previous location, it didn't make any sense because we weren't loading any entities. I moved it to the spot where we'er actually loading an entity. I think perhaps the code that it referenced was moved or refactored and the comment wasn't moved with it.
20/21. :)
22. Fixed.
23/24. :)
25. Good catch.
26. Yep, 🦅👁
#23:
1. Me too :P
2. Yes. I'll take a look into how/why
Comment #25
gabesullice#23.1: I posted a review here: #3014277-15: ResourceTypes should know about their fields. I was pleasantly surprised to find that these two patches will not interact much at all. Since it doesn't change the API of the
ResourceTypeobject, this patch can remain unchanged.#23.2: Extras is overriding
ConfigEntityNormalizerandContentEntityInterfaceso that it can apply its field enhancers. The most complicated changes are surrounding denormalizers. But we're only renaming classes for that use case, meaning it will just mean renaming some classes for Extras.The one other change to account for is that Extra's
ConfigEntityNormalizeroverrides thegetFieldsmethod so that it can apply its field enhancers on normalization. In this patch, I removed that method because it was moved onto theResourceObjectclass. To make it easy for extras, I'll just restore that method in a simpler form so we can preserve Extras' behavior.The last thing to be concerned with is that Extras only does this "field" enhancement in the config entity normalizer (content entity field enhancement happens in
FieldItemNormalizer). In this patch, normalization of config and content entities are handle by the same class. That's an easy problem to fix by performing an!$field instanceof FieldItemListInterfacecheck ingetFieldsthus skipping the enhancement for content entities.Comment #26
gabesullicePostponing this on #3022584-47: Consolidate and simplify NormalizerValue objects: introduce CacheableNormalization, which adds a @todo for this issue.
Comment #27
wim leers#3022584: Consolidate and simplify NormalizerValue objects: introduce CacheableNormalization landed!
Comment #28
gabesullice"Rerolled" is an understatement :P but that's what I did.
Comment #30
gabesulliceLooks like I missed a small update in
FileUploadTestand accidentally triggered a deprecation notice because of a private service. NBD.Comment #32
wim leersOh PHP… 😭
Comment #33
wim leersI don't have time to do a deep review of this today. But ideally the issue summary would explain what work this unblocks.
It's not in the issue summary, but #3.7 is citing improved performance as an important reason for doing that. But based on preliminary benchmarking (admittedly with only 4 nodes), I'm seeing a slowdown from ~87 ms for 100 requests to ~90 ms (
ab -c 1 -n 100 -H "Accept: application/vnd.api+json" http://d8/jsonapi/node/article, with Page Cache & Dynamic Page Cache disabled.)It looks like this does not block #3014277: ResourceTypes should know about their fields.
I'm sure I'm missing something, if you can just add a list of reasons why we should do this (benefits/unblocking) to the issue summary, that'd definitely help :)
Comment #34
gabesulliceSee #6 and #7 ;)
I updated the issue summary with some other callouts.
Comment #35
gabesulliceComment #36
gabesulliceReroll.
Comment #38
wim leersComment #40
wim leers#36 lost the fix from #32.
Comment #41
wim leers💪 Let's update the patch to do this. So that we can see the full set of benefits that this brings us. But let's first address what's below.
👍 So this is only ever called if
\Drupal\jsonapi\Access\RelationshipFieldAccess::access()granted access.This is just adding documentation to help future readers.
🤔 Hm … but I don't think we allow or ever intend to allow
class ResourceIdentifier implements ResourceIdentifierInterface, do we?Why can't we change this to
\Drupal\jsonapi\JsonApiResource\ResourceObject?🤔 I don't think we need to list
LabelOnlyResourceObjecthere, since it subclassesResourceObject. Previously,LabelOnlyEntitydid not implementEntityInterface, so it had to be listed separately.That makes us get quite close to an ideal state, because it means only 3 kinds of things are allowed: individual resource objects, collections of resource objects (for now still called
EntityCollection, but we'll soon be able to rename that toResourceObjectCollection) or a collection of errors.🤔 This … sort of does what I was getting at above: it omits
LabelOnlyResourceObject. But curiously, it checksResourceIdentifierInterface, notResourceObject.And apparently one potential type was missing from the docblock? Because we're also allowing
🤔 Remind me why we allow both a
ResourceTypeobject and string, and retrieve the object when necessary? Why not always require it to be passed in?You don't need to answer that on the issue, but documenting the rationale and its assumptions in this class allow us to more easily make this simpler in the future.
🐛 That's one purpose. Another purpose is to be able to remove the need to distinguish between config and content entities all over the JSON:API code base. Let's document that too.
🐛 Just
arraymeans I still need to guess whether this is field names (string[]) or field instances (FieldItemListInterface[]).🤔 Hm … why does this live on this class and not on
ResourceObject? Looks like this is only for the label only case in\Drupal\jsonapi\IncludeResolver::resolveIncludeTree().I think this makes sense for now, but I suspect we'll be able to remove it when we refactor
EntityAccessDeniedHttpExceptiontoResourceObjectAccessDeniedHttpException?✅ Can't this be simplified to an
array_intersect_key()call? We're repeating logic that the parent method already did. Done.✅ This is also duplicating logic from the parent class. It'd be better to move this instead then. Also: this should not have been
public. Done.✅ Nit: Can just be
{@inheritdoc}. Fixed.✅ Übernit: indentation is one space off. Fixed.
✅ This is already inherited from the base class, can be removed. Done.
✅ Nit: mismatch. Fixed.
🤔 This used to be clearly paired before. I mentioned this before in #22.17. You addressed it. But I think this is still fairly confusing. The array indices need to be kept in sync.
Perhaps #3024896: Remove Relationship and RelationshipItem classes will simplify this?
✅ Nit: entity -> resource object leftovers. Fixed.
🐛 Mismatch:
ResourceObjectvsResourceIdentifierInterfacevsEntityInterface.🤔 I'm not sure I understand the value of this change?
✅ This is already inherited, can be removed. Done.
✅ Mismatch. Fixed.
✅
These can now be static. Done.Actually, having these helper methods is pointless now, thanks toResourceObject! Removed.✅ Needed updating. Fixed.
In doing this, I made the patch slightly smaller :) Now the diffstat is:
869 insertions(+), 866 deletions(-). So: same LoC, but clearer code.Comment #42
wim leersAlso: if you roll the patch with
-M5%it will record the moves, making the patch not only a few K smaller, but also easier to understand. (659 insertions(+), 656 deletions(-))Comment #44
wim leersYay, #41 is still green, but it contains a single CS violation 😬 Easy fix.
Assigning to Gabe for points 1, 3–8, 15, 18 in #41
Comment #45
wim leersIdea for follow-up issue: add
::toUrl()method inspired byEntityInterface::toUrl(). This would remove the need for\Drupal\jsonapi\LinkManager\LinkManager::getEntityLink(): you'd doResourceObject::toUrl('individual')instead. Or even::toLink(), which would return a\Drupal\jsonapi\JsonApiResource\Link().(Shall I create a follow-up issue for that?)
Comment #46
gabesullice#41:
1. Will do in another comment.
3.
EntityAccessDeniedException implements ResourceIdentifierInterfacethough, so an interface is useful here. It means that none of the logic needs to know that a resource object is interchangeable with an exception.I actually do imagine that one day we might pass in
ResourceIdentifierobjects. AnEntityCollectionessentially represents a document's "primary data" and the primary data for a relationship response could easily be a collection ofResourceIdentifiersafter #3024896: Remove Relationship and RelationshipItem classes. A followup to this issue could be to renameEntityCollectiontoPrimaryDataand for individual responses not accept aResourceObjectbutPrimaryData(formerly,EntityCollection) with a cardinality of 1 instead. That would make individual documents indistinguishable from unary `related` documents except for their links.Given that this is all internal, I think it's fine to have this small bit of imprecision (hopefully temporary).
4. Removed
LabelOnlyResourceObject. About the ideal state, see above.5. For the most part, see #3 above. WRT to
EntityReferenceFieldItemListInterface, that's what we pass in fromEntityResource::getRelationship(ofc, that's also related to what I said above!)6. Honestly, I never even realized that myself. Good point!
7. Clarified.
8. Exactly.
15. Yep, it already did: #3024896-15: Remove Relationship and RelationshipItem classes
18. Whoops! I meant to take this concept further and forgot about it. Because resource type is carried by a resource object, there's no longer a need for a single request level resource type during normalization (see interdiff). This was a stumbling block here. With this code, a relationship normalizer "knows" about the parent resource object instead of the the parent resource type and that just feels more "right".
Tying this back into #3, I think this helps us conceptually unify normalizing a relationship's primary data and normalizing a resource object's relationship object. Why would that be cool? Beyond fewer concepts... partial caching. Normalizing either a resource object or a relationship object could help warm the cache for the other. That's really useful because normalizing relationship objects requires that we load all the referenced entities in a really inefficient way:
and this:
You've mentioned this before and I did it for this patch. I often see a small reduction in size, but I rarely find it easier to understand. I wonder what's different... Perhaps our diff algorithm is configured differently or perhaps we review patches differently?
Sure. I'm not 100% about the idea yet, but we can have that discussion in that issue. I think it's sort of contrary to #41.1
Comment #48
wim leersFile renames and/or moving significant pieces of logic are shown as moves rather than additions and deletions. It's super difficult for humans (at least this human!) to go through 100 lines of deletions and 100 lines of additions and verify that only a few LoC actually changed. It's much easier to see this when the diff only contains those few LoC that actually changed. 🙂
Comment #49
gabesulliceI meant, I wonder how our review process differs, not how an
-M5%patch is different from one without it.Look at Dreditor's diff for
EntityDenormalizerBase.php, that's a good example of where I think that flag goes wrong. The deletions/additions bear no relation to one another making it impossible for me to grasp what actually changed, I'd honestly rather look at the whole file and compare it by eye than reverse engineer that mess.Comment #50
gabesulliceThe #46 failure was because I just needed to update a few
->getIncludes()that I didn't resolve correctly when merging #3026030: [regression] Includes are no longer respected when POSTing/PATCHing.That made me take a closer look at
IncludeResolverwhere I realized it's no longer necessary to pass it a$base_resource_typeor$relatedfield name. This is one of the kinds of simplifications I was hoping would become apparent when this was done :)Comment #51
gabesulliceAlright, this addresses #41.1 by adding a links argument to the
ResourceObjectclass. It's an optional argument and aselflink is generated by default. It was nice to see how theLinkCollectionobject just plugged right in. Because resource objects now carry their ownselflinks, I was able to get rid of another usage of the link manager inResourceObjectNormalizer(formerly EntityNormalizer).After that, I realized that having a
selflink always present would make thetoUrlthing really easy to do. So, I went ahead and implemented it. Of course, that led to get rid of another usage of the link manager inEntityResource🎉. Which of course made me curious... "where else is the link manager being injected?" I found that it is already unused in a place or two and so I just went ahead and removed those too.If #3024896: Remove Relationship and RelationshipItem classes lands, this all means that there will only be a single service that depends on the link manager after this! That's
EntityResource, which only uses it for getting the top-level document'sselflink (this could easily just be inlined intoEntityResourcenow) and for getting pagination links. The pagination method is a little more complicated, but I don't think there will be a strong argument for keeping it separated from the collection query logic after this.Comment #53
gabesulliceComment #55
gabesulliceAh, internal entities don't have routes either.
Comment #56
wim leers#46:
#49: I usually use 5%, but yes, sometimes you need to tweak that to, say, 20%.
#50: 🎉🎉🎉 — I've always found that super confusing! This interdiff is full of win!
Comment #57
wim leers#51/#41.1: Your first paragraph there describes everything I expected and hoped 😀 👏 Everything else sounds like the hard labor of our iterating towards maintainability and simplicity is paying off :)
😍
👍 Apparently these were already obsolete service dependencies, fascinating.
🤓✅ Nit: Let's bring a similar assert here too.🤓 Isn't this just
Resource object's links.?🤔 Pondering … why/when is a
selflink already provided?👍 Given the benefits this brings above, and given that this is still an
@internalclass, I'm fine with this.Overall diffstat:
32 files changed, 757 insertions(+), 761 deletions(-). LoC = status quo. But it brings more simplicity, and has stricter code (moreassert()s) as well as better-documented helper methods that will surely prove useful elsewhere in the future too.I reviewed it in detail. I think this is ready, because it's an iteration on the internals of our code that I believe sets us up for success in the future. Zero functional tests needing to change proves this is purely internal.
Comment #58
gabesullice#56
15. I was going to just address your comment in this issue by renaming the variables, but I found a bug while doing so. Because of the bug, I opened a separate issue which is postponed on #3024896: Remove Relationship and RelationshipItem classes. Then, if that issue doesn't land, we can fix the bug and clear up the confusion in that issue. If it does land, then we can close the issue as outdated.
#57
3. Heh, I had exactly this and removed it because it's just duplicating code since it's immediately check in the constructor. OTOH, this will be good for self documenting reasons. Most probably won't bother to look at the constructor esp. since
$contextis an "internal-only" argument. 👍4. Yep. Fixed.
5. It isn't. Perhaps this is designing without an existing use-case, but I felt that it would be better design to respect, not override, any links that were provided. Perhaps if we decide to bring
jsonapi_hypermediato the main module some day, the pattern will be to let links be overridden by these constructor$linksarguments rather than during the normalization phase. In that case, I think it's conceivable that one might want to override theselflink as part of a strategy to create "pretty" URLs. For a commerce site, maybe you'd want product variations to have a more hierarchical URL like/jsonapi/product/shirt/some-uuid-for-shirt/colors/uuid-for-blue-variantvs./jsonpapi/product/variant/uuid-for-blue-variant.Comment #59
gabesulliceThis should fix 8.7.
Comment #60
gabesulliceRTBC and passing on 8.5/6/7!
I think this only needs @e0ipso's +1
Comment #61
gabesulliceThis blocks #2992836: Provide links to resource versions (entity revisions), so bumping the priority.
Comment #62
wim leersCan you update the issue summary to state that as one of the concrete benefits?
Comment #63
e0ipsoSorry for not jumping in earlier. Just letting everyone know that this is being discussed in Slack. So progress is happening.
Comment #64
wim leers#3028970: Audit all @todos, #3030148: Clean-up: remove dead $formats property from normalizers and #2965056: Support `include` parameter on relationship routes (turns out it accidentally works on cold caches: fix + test this) conflicted with this, rerolled. (I can't wait for RTBC retesting for contrib modules…)
Comment #65
wim leersIn #23, I pointed out which things would break https://www.drupal.org/project/jsonapi_extras. @gabesullice responded in #25 with the changes JSON:API Extras would need to make. Let's provide that patch here, @gabesullice?
Since https://www.drupal.org/project/consumer_image_styles/releases/8.x-3.x-dev requires
jsonapi_extras, there is no way to test that yet either.Comment #66
wim leersFor #65.
Comment #67
gabesulliceNeeded a reroll after #3024896: Remove Relationship and RelationshipItem classes
Comment #69
gabesulliceWhoops, missed something in #67
Comment #71
gabesulliceAlmost there.
Comment #73
gabesulliceThis should be back to green.
Up till now, I've been setting this back to RTBC since I was making fairly small, unobjectionable changes. This is a little bit bigger of a change, so I'll let you put another set of eyes on it.
Comment #74
gabesulliceWoot! Whew, that was a challenging rebase!
I've just posted a "patch" for JSON:API Extras here: #3032451-12: Compatibility with upcoming JSON:API 2.2 release: JSON:API Extras 3.4. Unfortunately, I can't post a self-contained patch in this issue because it relies on other pieces of the compatibility patch in that issue.
Comment #75
wim leersConfirmed that with #3032451-12: Compatibility with upcoming JSON:API 2.2 release: JSON:API Extras 3.4, JSON:API Extras works. Consumer Image Styles doesn't work yet though, working on a patch for that.
Comment #76
wim leersVerified that this unblocks #2992836 — patch at #2992836-27: Provide links to resource versions (entity revisions).
Comment #77
wim leersVerified that this does not conflict with #3014277 — see rebase + comment at #3014277-17: ResourceTypes should know about their fields.
Comment #78
wim leersI reviewed this patch once more.
This made me suspicious: why is this necessary? So I went to look at
EntityReferenceFieldNormalizer. It had this extra dependency injected since #28. It was used ingetTranslatedResourceObject().But as of the #67 rebase,
getTranslatedResourceObject()is gone, yet the service is still injected.Either the service can be removed, or this is a bug in the current patch. But the earlier patch iterations had this:
Indeed it doesn't make sense to get the translated entity reference — it's still the same entity that is being referenced.
And finally, the line after that:
That access checking is not something HEAD does either.
Conclusion: it's fine to remove the entity access checker from
EntityReferenceFieldNormalizeraltogether, and in doing so it matches HEAD's behavior.Comment #79
wim leersForgot one hunk :)
Comment #82
wim leersComment #83
wim leersThat failure in 8.7 must be related to #3031750: Drupal core compatibility: User `mail` field varies by the ‘user’ cache context in Drupal >= 8.7.
Comment #84
wim leers(And that in turn was caused by a change in Drupal 8.7 core.)
Comment #85
wim leersBesides another detailed review, I've also analyzed impact of this on various things:
ResourceObjectjust like in the JSON:API spec and bringing more clarity to the rest of the code base makes it a win-win!\Drupal\consumer_image_styles\Normalizer\ImageEntityNormalizerdoes). But he pointed out that a far simpler solution is now possible: change it to aLinkCollectionnormalizer that decorates the default normalization only when a particular type of entity with a particular kind of value is present — because all this normalizer does, is adding links! So it will serve as a further validation of #2994193: [META] The Last Link: A Hypermedia Story.Assigning to @gabesullice for that last point.
Comment #86
gabesulliceI've confirmed the change in #78.2 is fine. That code has its roots in #2794431: [META] Formalize translations support. However, it should have been removed in #2997600: Resolve included resources prior to normalization since include resolution now happens outside the normalization system. Access/translation handling is handled in the
IncludeResolveralready, so we lose nothing by removing it.Given that @Wim Leers already reviewed everything once and I've confirmed the changes he made, marking this RTBC.
Now going to work on the consumer_image_styles patch.
Comment #87
gabesulliceAh, I forgot about the 8.7 failure in #82 (and before).
After digging in, it looks like this failure was already anticipated in #3022584: Consolidate and simplify NormalizerValue objects: introduce CacheableNormalization, note this removed hunk:
That hunk is referring to this issue!
HEAD is wrongly associating the cacheability of every field even when only the label field is being normalized. That's why we didn't notice this failure in #3031750: Drupal core compatibility: User `mail` field varies by the ‘user’ cache context in Drupal >= 8.7.
TL;DR:
This patch is more correct WRT to field cacheability than HEAD, so we need to update our test expectations for that.
Comment #88
wim leersAh, yes, of course, I remember discussing that! Perfect!
Per #85, I think we should commit this as soon as we have a patch that updates
consumer_image_stylesto keep it working.Comment #89
gabesulliceThis fixes a type hint and a small pre-existing bug in
LinkCollectionthat was revealed while I was working on #3032468: Compatibility with upcoming JSON:API 2.2 release: JSON:API Extras 3.4Comment #90
wim leersI just manually ran tests for Consumer Image Styles with #3032468-4: Compatibility with upcoming JSON:API 2.2 release: JSON:API Extras 3.4 applied and using #87. I reproduced the bug that #89 fixes. So it all checks out, and now the last thing of #85 has been addressed!
Let's do this! 💪
Comment #91
effulgentsia commentedWhile we're renaming anyway, any reason not to move this to the
JsonApiResourcenamespace, where the base class and other similar classes are?I'm fine with that to be a follow-up if this is otherwise ready for commit.
Comment #92
wim leersWELL SPOTTED! 🧐
Comment #94
wim leersOne silly mistake.
Comment #96
wim leers🎉
Comment #97
gabesullice🎉🙌
Comment #98
wim leersThis unblocked #2992836: Provide links to resource versions (entity revisions) and #3032679: Clean-up: rename EntityCollection to Data.