Problem/Motivation
While reviewing/working on #2922121: When fetching related resources, getResourceType() in CurrentContext gets the wrong resource type., I remembered that we still have #2930228: Refactor CurrentContext away in the queue.
An important step for this is to allow "configuration" for the current route to be available to the normalizers and EntityResource. This is essentially what CurrentContext does in an obfuscated way.
Then, I remembered this recent improvement in REST: #2935783: RequestHandler's handle method interacts with the Route object but should be able to just declare arguments
Some discussion of this this can be found in: #2932679: Remove unused "on_relationship" serialization context
Thus, both of these issues are essentially solved/outdated:
- #2930228: Refactor CurrentContext away
- #2922121: When fetching related resources, getResourceType() in CurrentContext gets the wrong resource type.
Proposed resolution
TL;DR, this removes the CurrentContext and all access to the current route match/object. It does this by parameterizing the resource type in an idiomatic way.
Add a ResourceTypeParam converter and pass the resource type to RequestHandle so the controller does not need to interact with route object or current context.
By turning the resource type and target serialization class into route parameters, we can completely remove every dependency on CurrentContext. The same changes make it possible to purge access to the route object and the current route match (except in the enhancers, where that still makes some sense).
API Changes
None. Because everything is internal. However, there are some notable changes to the internal API:
CurrentContextis no longer a service one can replace, for w/e reason one might have been doing that.FieldResolver::resolveExternalis removed (it was completely unused in the module).
(this removes far more code than it adds 🙃)
| Comment | File | Size | Author |
|---|---|---|---|
| #19 | 2941685-15.patch | 48.08 KB | gabesullice |
Comments
Comment #2
gabesulliceComment #3
gabesulliceComment #4
wim leersThese statements sound like music to my ears!
This I don't understand.
\Drupal\jsonapi\EntityToJsonApi.!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
This is clear proof this is a vast simplification.
This will definitely need sign-off from @e0ipso.
Initial patch review:
😍
I love how simple this is!
Nit: keeping
addDefaults($defaults)here would make the change slightly simpler to understand.Can we avoid this change? This feels out of scope.
Comment #5
e0ipsoAs long as things are working as expected in all supported core versions I'm not opposed to code simplifications. Besides, this feels like a clean way to avoid CurrentContext (which was created under the expectation to have to manage complex application states and proved to not be the case). 👏🏽👏🏽
However I worry a bit about priority. Sorry to sound like a broken record, but this seems like it did not move us towards any of the issues in #2931785: The path for JSON API to core. I think our laser focus should be features we need to get in (like #2932035: ResourceTypes should be internal when EntityType::isInternal is TRUE, #2887313: JSON API indicates it supports POST/PATCH/DELETE of config entity types, but that's impossible, …), test coverage (like #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only) and bug fixes (like #2935947: Comply with code standards).
Once those categories are depleted I think our focus should be on getting a core comitter / fwk manager / whatev to start taking a look to the module. Then, in parallel, we can knock off these code refactors.
In summary, I think issues like this are very important but I'd like to get away from the core committer mind-reading so we can move steadily towards the goal to get this guy into core.
Comment #6
gabesullice@e0ipso, don't feel bad about beating the drum for focus and priority. I share that sentiment too :)
FWIW, I think the scope of the change misrepresents the actual effort involved (writing the patch, at least). It came together quickly. As @Wim Leers pointed out, I was mostly me deleting code. The rest was just recycling code/knowledge about routing params gained in #2939722. Finally, as mentioned in the IS, this did start as a bug fix. I just saw a way to kill two big birds with one stone :)
Comment #7
gabesulliceI think it's a bit of a code smell to be accessing the route match, that's all. 90% of the time, access to the current route can be refactored into a parameter, which makes the code simpler and requires less testing set up. These changes pave the way to clean some of that up.
No. Why would it?
I will do this in my next comment.
Nope.
To which he said: "As long as things are working as expected in all supported core versions I'm not opposed to code simplifications. Besides, this feels like a clean way to avoid CurrentContext"
@e0ipso raised a valid concern about priority, so let's not take too much time for this and stop if it gets out of hand. So far, it's been less work than I'd have imagined.
This issue is about using route parameters over route options in order to configure the behavior of the controller, not just removing CurrentContext (although that's the best side-effect!). So while the change is minor, I think it's in line with the rest of the patch. Hopefully my next comment will show why it changed.
Comment #8
gabesulliceBy introducing a param converter, we can directly load the primary resource type and pass it to the RequestHandler controller. That means we can remove some dependencies and hoop-jumping I'll point out later.
Need I say more?
Removing the
resolveExternalmethod from the field resolver let the field resolver stop relying on current context. This was dead code (or rather code that we just assumed would get used, but in the end we never did).The only reason we were injecting the route match was to get at parameters and to get the serialization class.
But the request already has those parameters. So, like a good controller should, it's getting its parameters from the request. By doing so, we remove a layer of indirection. That makes the flow easier to understand and to test.
That's what this patch is about :)
Again, using the route match to get the route object was just a layer of indirection.
See? ;)
By making the resource type a parameter, we can build the
EntityResourcefrom aResourceType, because it already has all the information we need.By setting the resource type and "on_relationship" piece as serialization
$context(which we can now do because we have that info as request params), the normalizers no longer need theCurrentContextservice to get the same information.This will untangle a number of issues that have cropped up in the past and probably are still lingering, where the current context returns the wrong resource type because it (ironically) doesn't "know" which context the normalizer is in (on a relationship, an include, etc.)
This is what I'm talking about.
The entity UUID converter was taking over for all params in the route, but it can't do that now that the resource type param converter needs to handle something too.
All of these mocks can be removed from the tests ❤️
Comment #9
gabesulliceComment #10
wim leers#7:
Because >50% of the code in that class is dealing with simulating "current context".
#8: 😍 — thank you, that helps a lot!
The fact that #7 is passing today means it's passing with #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only's tests also passing. I'd like to hold off on committing this until #2932031: Comprehensive JSON API integration test coverage phase 2: for every entity type, complex use-cases is in too, because that gives us even stronger guarantees this is not breaking anything. Those two issues combined mean we can be 100% certain at least individual requests still work as they did before. Collection requests, includes, sparse fieldsets etc do not yet have similarly comprehensive integration tests though, for that we have #2945093: Comprehensive JSON API integration test coverage phase 3: test JSON API-specific use cases: related/relationship routes, includes and sparse field sets. Should we wait for that to happen before committing this? It seems that's preferable, because more prudent. At least until some important bug is reported that would be fixed by committing this issue — when that happens, I definitely wouldn't want to wait for #2945093 to land first anymore.
Comment #11
gabesullicePerhaps this was accurate in the past, but I don't think that's accurate now. AFAICT, all it does is prepare the serialization context, which is not the same as simulating "current context".
Perhaps you're suggesting that it should be using another mechanism to generate a document, like a subrequest? If so, I think that's out of scope here.
I'm 80% certain that this will fix #2922121: When fetching related resources, getResourceType() in CurrentContext gets the wrong resource type. as mentioned in the IS. Perhaps replicating that issue in the complex test cases could be one of the first things you try, @Wim Leers? My hope is that those tests will actually reveal the bugs that will be fixed by this. I'm sure those bugs are lurking there in the corner cases; we just need to hit them with a spotlight.
Comment #12
wim leersOops, you're right! (Side note: we have too many contexts: Symfony/Drupal request context, Drupal context, Drupal cache context, Drupal render context, Symfony/Drupal serialization context and JSON API current context. It's great to see this issue removing one of them!)
That sounds wonderful! Does that mean we want to officially mark this postponed on #2922121? (Also — please see #2945093-3: Comprehensive JSON API integration test coverage phase 3: test JSON API-specific use cases: related/relationship routes, includes and sparse field sets.)
Comment #13
wim leersI think it's probably best to postpone this on #2945093: Comprehensive JSON API integration test coverage phase 3: test JSON API-specific use cases: related/relationship routes, includes and sparse field sets, which is SUPER close to getting committed :)
Comment #14
wim leersPer #2883086-33: [PP-1] Port RequestHandler + ResourceResponseSubscriber improvements from REST module to JSON API, this is a blocker for that issue, and this should go in after #2935370: Mark the JSON API serializer, normalizer and encoder services as private.
That means this is now PP-2: blocked on both #2935370 and #2945093.
Comment #15
wim leers15 files changed, 147 insertions, 482 deletions.15 files changed, 145 insertions, 488 deletions.Pretty much the same, as expected!
The "combined" patch includes #2935370-24: Mark the JSON API serializer, normalizer and encoder services as private, which is currently RTBC.
Comment #16
wim leers#2935370: Mark the JSON API serializer, normalizer and encoder services as private landed. This is just blocked on #2945093: Comprehensive JSON API integration test coverage phase 3: test JSON API-specific use cases: related/relationship routes, includes and sparse field sets now, which is also RTBC!
Comment #17
gabesullice#2945093: Comprehensive JSON API integration test coverage phase 3: test JSON API-specific use cases: related/relationship routes, includes and sparse field sets landed, this is now unblocked 🎉
Comment #19
gabesulliceJust re-uploading the patch now that the blocker is in and we no longer need a combined patch. No changes since #15.
Comment #20
e0ipsoAfter a review of the code I cannot see the feature, the bug or the benefit of this change. I'm struggling to see the benefit of this patch. This is a nice refactor, but I don't feel is necessary at this point. I'm leaning towards taking the same route (pun intended) that we went with in #2932679: Remove unused "on_relationship" serialization context. Let's postpone for #2842148: The path for JSON API to "super stability".
Comment #21
gabesullice"145 insertions, 488 deletions"
I'm sorry, but I really must beg to differ. This is a significant simplification of the way routes are handled that decreases complexity throughout the module. It's gone through multiple reviews and has passed tests before and after landing all the additional test coverage. Without seeing any specific reservations about the patch, it appears that the only thing being postponed is the commit itself.
It's not clear to me why we can't commit fixes for that issue today.
Comment #22
gabesullicePlease also remember that this issue blocks #2883086: [PP-1] Port RequestHandler + ResourceResponseSubscriber improvements from REST module to JSON API
Comment #23
wim leersI agree with @gabesullice, because:
jsonapi.current_contextservice:The latter two are bug reports that have been open for 4 months! This patch solves them.
This alone is a good sign: our
RequestHandlerneeds so much to be injected, removing a service dependency confirms this is an important simplification!Not to mention that there is a very real risk core committers will require that this class gets fewer services injected, because it is currently too complex. It has currently 8 dependencies injected, compared to only 2 in core's
\Drupal\rest\RequestHandler!And this. This was modifying what amounts to global state. The removal of this line proves global state handling is removed. That is a huge risk removed.
I hope you will reconsider, @e0ipso. I do agree with your request for relentless focus on #2931785: The path for JSON API to core and nothing else. But this too presents a significant risk.
That being said, upon re-reviewing, I found a few nits:
Nit: This should not be deleted.
Nit: Is this change really necessary? It seems this could remain as-is? That'd reduce the amount of change here: it'd mean that 100% of the changes are related to "current context".
Comment #24
wim leers#2883086: [PP-1] Port RequestHandler + ResourceResponseSubscriber improvements from REST module to JSON API has been RTBC for a week, but is blocked on this. Let's get this done!
Comment #25
wim leersThis is still blocking #2922121: When fetching related resources, getResourceType() in CurrentContext gets the wrong resource type. and #2925043: Server error when using the jsonapi.entity.to_jsonapi service. It'd solve both of those bugs. Hence bumping to .
Comment #26
e0ipsoI still think that we should not keep on changing things that work. I won't go into a debate on the different points in the comments above, because that's an awful use of the time of everyone here, given that everyone's position is clear :-P. I am going to merge this and tag a release with it.
However I'd like that you realize that refactors, that just change the way things are implemented into a way that you like better, have other implications. They require a free time that is scarce to me to review, they break things in related modules (JSON API Extras, Consumer Image Styles, Contenta CMS integrations, …) that then I need to fix in that scarce free time, they also pose a risk of unexpected regressions (don't underestimate the tested by production usage factor), etc. Since I don't have full time paid dedication on this, my options are either to ask for this, become a blocker here and in other projects, or step aside. I think that I can keep up if we postpone non-critical refactor for the future (I'm not saying they shouldn't happen). Also, please remember that arguments like complexity, usability, maintainability, reliability, etc. are most often subjective.
I hope you appreciate the honesty in my feelings here, and take it as a plea rather than criticism. This is my attempt to keep on being passionate about this and avoid burnout.
With that said, I think that this patch is an improvement over the existing code and I am merging it.
Comment #29
wim leersThanks to this issue, we fixed two bugs (#2925043: Server error when using the jsonapi.entity.to_jsonapi service and #2922121: When fetching related resources, getResourceType() in CurrentContext gets the wrong resource type.) and have less code to maintain (#2930228: Refactor CurrentContext away). If we didn't fix bugs in the process, I'd agree with not making this change!