Problem/Motivation
Drupal should use modern PHP attributes for route discovery a la Symfony.
This will allow us to add an attribute to a method name and use that for routing information. For example:
/**
* The default 401 content.
*
* @return array
* A render array containing the message to display for 401 pages.
*/
#[Route(
path: '/system/401',
name: 'system.401',
requirements: ['_access' => 'TRUE'],
defaults: ['_title' => 'Unauthorized']
)]
public function on401() {
return [
'#markup' => $this->t('Please log in to access this page.'),
];
}
will replace the following entry in system.routing.yml
system.401:
path: '/system/401'
defaults:
_controller: '\Drupal\system\Controller\Http4xxController:on401'
_title: 'Unauthorized'
requirements:
_access: 'TRUE'
What are the advantages of doing this?
- The
defaults._controller value is determined by the method you add the attribute to
- The route definition and the controller code live side by side
- No longer need route names - these can be automatically provided based on the class and method name
- Less difference between modern Symfony and Drupal
What are the disadvantages?
- The
routing.yml file can give a good overview of what a module does and makes reviewing route access a bit simpler
Mitigations of the disadvantages:
- Currently the code is scanning all the classes in a module's
src/Controllers directory for attributed methods - therefore it's not that difficult to scan this code and you benefit from the controller and route definition being together.
- We should and can build CLI tools to expose all the routes provided by a module. This would be good not just for routes determined by attributes but also dynamic routes
Proposed resolution
- Remove yaml discovery from the RouteBuilder into it's own service
- Add a new static routing event for the yaml discovery to listen to
- Add a new service for PHP attribute discovery and listen to the same event. This reflects on all classes in a module's src/Controllers directory to find routes
Remaining tasks
Add more test coverage
Decide which of the Symfony route features to keep in - env, localized_paths, priority, _locale, _canonical_route stuff... not sure what to do with this.
User interface changes
None
API changes
- Symfony's
#[Route] attribute API can be used on controllers.
- A new
RoutingEvents::STATIC phase handles static route generation, including parsing YAML and controller method attributes
Data model changes
None
Release notes snippet
None
Comments
Comment #3
alexpottI've torn \Symfony\Component\Routing\Loader\AnnotationClassLoader apart and added the relevant stuff to our RouteBuilder... ideally we'd add an AttributeDiscovery and use that as well as the YamlDiscovery stuff...
But at least this is a start...
Comment #4
alexpottSetting to needs work to reflect that all the code needs to be refactored into a more sensible structure.
Comment #5
alexpottAdd some detail to the issue summary.
Comment #6
dpiAs I mentioned in #3317792-11: Make controllers invokable for DX and maintainability , we should make sure our implementation works with classes (tests!), as the
Routeattribute we are using is\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD.As in, working Symfony code:
Comment #7
andypostThat's what I faced with in #3159210: Support route aliasing (Symfony 5.4) and allow deprecating the route name
Aliases should replace alias route name with a name from target route in route match.
So if no route names available then how to alter routes? I mean the class name will be hard to deprecate instead of yaml file
Comment #8
andypostPHP 8.1 and 8.2 both green using last patch, patch needs some polishing of docs but mostly looks ready
Route rebuilding is not much often happening so no reason to measure performance effect on descovery
PS: not sure is that a task or feature
Comment #9
aaronmchaleThis is a great idea! +1
I actually don't think that the problem is unique to this issue, it technically already exists thanks to Route Providers and Enhancers. I'm thinking Entity Types which have Route Providers and routes which are added by Route Enhancers (e.g. Field Ui and Layout Builder). In most cases the routing.yml file already does not provide a good overview of all the routes associated with a given module, so if anything the solution here just continues this existing pattern.
This is also a good idea, and as I noted the current situation would definitely benefit from this. Maybe that would help gather momentum for #3089277: Provide core CLI commands for the most common features of Drush.
Comment #10
kim.pepperCan we use Psr4DirectoryLoader? See https://github.com/symfony/symfony/blob/6.2/src/Symfony/Component/Routin... and https://symfony.com/blog/new-in-symfony-6-2-psr-4-route-loader
Comment #11
alexpott@kim.pepper nope we can't. We've not got the Symfony config component for lots of reasons.
Comment #12
alexpottAdded some of the necessary change records. Need to improve the main CR - https://www.drupal.org/node/3314769.
I think we need to add unit test coverage of the new classes: AbstractStaticRouteDiscovery and AttributeRouteDiscovery.
Comment #13
alexpottActually there's very little logic in AbstractStaticRouteDiscovery so I don't think an individual test of that would give us much but a unit test of AttributeRouteDiscovery would be good.
Comment #14
alexpottOne choice we have to make it whether to keep the plumbing in for environment based routing. In Symfony you can define routes that will only be available in certain environments but we don't leverage that at all but the plumbing to do that is in AbstractStaticRouteDiscovery and AttributeRouteDiscovery. I think we should consider supporting the feature but then we'll be in #1537198: Add a Production/Development toggle territory. I think I'm in favour of removing the code and opening a separate issue to work out how to do things like this.
Comment #15
catchWhy an event and not a tagged service?
Don't think we need ordering, ability to stop propogation etc.
Comment #16
smustgrave commentedThink the MR got altered. Seeing 900+ changes for ckeditor, composer, etc. Is that expected?
Comment #17
kim.pepper@smustgrave seems ok now. I saw something similar on a different issue. I think its a gitlab problem.
Comment #18
smustgrave commentedWill add to my list.
Comment #19
alexpott@catch / #15 I went with an event due to consistency with the existing routing building events.
Comment #20
kim.pepperI can take a look at #13:
Comment #21
joachim commentedI've opened an issue to decide on where to put annotation classes, so all annotation issues can work to a common standard: #3344303: [policy, no patch] Decide on a namespace for PHP Attribute classes.
Comment #22
kim.pepperA couple of extracted comments from @alexpott and @berdir in slack:
Comment #23
smustgrave commentedBefore reviewing can the remaining tasks be crossed out for what has been completed please.
Comment #24
kim.pepperI've added more test coverage so crossed that out. Not sure if it's enough test coverage though.
Comment #25
smustgrave commentedSeems to have some open threads.
Comment #26
kim.pepperAddressed feedback.
Comment #27
andypostLeft a review and nitpicks, not sure core need to adopt sophisticated logic from SF with automatic route name generation
Somehow it discovery should report about routes without name
Comment #28
smustgrave commented@andypost think there are some questions in the MR for you? There are still some open threads.
Comment #29
smustgrave commentedComment #30
alexpottWhy not? Naming thing with IDs opens the possibility of namespace conflicts. Less hard coded IDs would be a good thing.
Comment #31
andypostI mean if route name will be generated from class-name or whatever it will be DX-- to use such names in URL generator and making links
Comment #32
joachim commented> Why not? Naming thing with IDs opens the possibility of namespace conflicts. Less hard coded IDs would be a good thing.
All it means is that instead of having a code ID that shouldn't change, you have a class that you can't rename.
Routes need to be referred to by other routes, child menu items etc etc.
I actually think the introduction of route IDs in D8 was a really good thing -- much easier to deal with route IDs rather than paths.
Comment #33
johnpitcairn commentedAs a custom module developer and site builder, I think I'd be fairly averse to auto generated route names based on the controller class.
Will this be optional? Would it include the namespace? I might have more than one DownloadController across a suite of modules for example.
Comment #34
claudiu.cristeaWhat about
_title_callback?Comment #36
donquixote commentedPersonally I think I like automatic route names based on the class name.
Obviously it should include the namespace.
But if we want to keep custom ids, one pattern that can be useful in combination with attributes is class constants.
Then other code can target that constant to refer to the route.
Of course, this again makes that code dependent on the class holding the constant.
To avoid that, the constant could live in a separate class..
A big question is also whether we want to convert the other yml files to attributes, e.g. for local tasks, menu links etc.
And as a consequence, will we refer to routes more often from yml files or from php code?
In yml files we cannot use constants (afaik), we cannot use class imports, and string ids generally look nicer than class names in yml.
In php code, I prefer class constants over string literals.
Comment #37
aaronmchaleI wonder if we could have local tasks, menu links, etc, also defined in the controller attribute. When creating a controller, and you want a local task to appear, it might be quite nice DX to just simply attach an attribute to the controller, rather than having to edit YAML files and have to manage the references. If things like local tasks can be defined in an attribute with the controller, that also simplifies it slightly because then you don't need to know or care what the route name is.
Comment #38
andypost@AaronMcHale great idea, ++ to add to summary and probably as follow-up issue
Comment #39
claudiu.cristeaMissing hook_menu()? :)))
Comment #40
joachim commented> Personally I think I like automatic route names based on the class name.
> Obviously it should include the namespace.
It's not explicitly stated in the BC policy, but route names have to be public API so that you can work with them to generate links, add extra pages to the UI, etc.
Automatic route names would mean the controller class name becomes API. I'm not automatically against that, but it's not what we're used to.
Also, it would worsen DX because how is a developer supposed to find the route name?
Comment #41
aaronmchaleBuilding on my previous comment, and the concerns about automatic route naming. If we're going to use attributes anyway, then why not just define the route name in the attribute.
That would address the BC and DX problems stated above, automatic route naming sounds nice in theory but it doesn't sound like it's worth it for the trade-offs mentioned.
It could also make it harder for new developers to understand what's going on, if the route names are generated automatically, we're abstracting away a key piece of information that could really help someone new understand routes.
The big DX win here is the ability to define the route (and potentially links, etc) right there in the controller class. Just that in itself is a huge win!
Comment #42
dakalaNot sure if this is irrelevant but a contrib module is already using PHP attributes for hook discovery - https://www.drupal.org/project/hux
Comment #43
joachim commented> If we're going to use attributes anyway, then why not just define the route name in the attribute.
+1
> Missing hook_menu()? :)))
Hmmm yes and no! Having to mess about in THREE different Yaml files to add a new admin tab is tedious. But hook_menu() wasn't simple, and there were plenty of ways that could go wrong too. I seem to remember complications around the MENU_DEFAULT_TASK value or something like that.
So I'd cautiously say yes to some sort of additional attributes to say 'Put this route in the menu' and 'Put this route in a tab set'. But as a follow-up issue - let's keep this one reasonably simple, and allow addition of future attributes when we design it.
Comment #44
joachim commented> The defaults._controller value is determined by the method you add the attribute to
I don't want to add complexity to this issue, so it should be a follow-up, but we should bear in mind in the building of this that we might want to be able to also use a Route attribute on a form class, where defaults._form would be the route property that gets set to the form class, rather than defaults._controller set to the method.
Comment #45
donquixote commentedI support it. But this should be a different attribute class.
A question is do we "pollute" the route list with these meta keys, or do we collect a different kind of item that we then use to generate routes _and_ links?
Comment #46
alexpottUpdated MR to be from 11.x and updated all the deprecations to be for 10.3.0 as this is not going to make 10.2.0
Comment #47
alexpottPOTX can read attributes - yay - so I think this is ready... going to add back the conversion of a single route. I think this issue is ready FWIW.
Comment #48
wim leersVery interesting — this looks indeed … very ready. Posted a few questions on the MR :)
This is true for not just routes, but also for all plugins of any given type: that is a frequent challenge when working on migrations (), validation (), and more.
Comment #49
alexpottI've added code to not support certain things (locale and localized paths) form the Symfony route attribute. I guess there is a question about whether we should have our own attribute... but it is kinda nice to not have our own. Not as sure as I once was :)
Comment #50
andypostRelated may need some work too
Comment #52
duadua commented@alexpott
You created two change records:
- https://www.drupal.org/node/3324758
- https://www.drupal.org/node/3314769
Neither of them had any information in it, so I started filling out some information on the first.
Is there a reason for us to keep the second one around?
Comment #53
alexpott@duadua I think I got a bit change-record-keen :)
I really like the idea of copying Symfony 6.4 and adding an always there FQCN route alias. That would mean getting #3159210: Support route aliasing (Symfony 5.4) and allow deprecating the route name done. Definitely think it is worth a follow-up. I wonder if we should forgo the automatically generated names like 'MethodRouteLocale' and instead where no name is provide automatically name using the FQCN that Symfony would create an alias for?
Comment #54
duadua commentedAdded https://www.drupal.org/project/drupal/issues/3401172
Comment #55
duadua commentedFor a while I was trying to figure out whether there is a logical space in which the slightly different title might exist.
I tried to delete the particular change record, but there are some additional permissions required. Someone will probably see the updated title.
Comment #56
duadua commentedWhat feels very sensible for me. Looking at the current way its generated, a FQCN name would be no way longer compared to the current autogeneration.
Looking at this it just let me realize that auto-generation has the advantage of avoiding accidental re-definitions of the same route name.
Looking at the schema for the router, I see there is a limit of length for the route name. Looking at everything, 255 seems plenty of space though for:
Comment #57
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #58
guptahemant commentedCouple of scenarios which comes in my mind, which we should address:
Comment #59
wim leersWould love to see this happen, this would be a massive DX improvement! Bumping priority 😇
I suspect this is also a de facto hard blocker for #3317792: Make controllers invokable for DX and maintainability ? 🤔
P.S.: would also help Experience Builder: #3490069-13: [upstream] [PP-1] Auto-register XB's controllers' invokable services.
Comment #60
larowlanComment #61
larowlanComment #63
longwaveRebased, needs the deprecations bringing up to date again.
Comment #64
longwaveRe #22 and the test failures I wonder if we should just allow strings only in _title for now, and move to TranslatableMarkup there in a followup?
Comment #66
longwaveRe #22/#64 actually I think the fix is simple, if
_titleis already TranslatableMarkup then we can use it directly; there is no need for a separate_title_argumentsor_title_contextwhen we are using attributes. If we want to remove that_raw_variablesusage inTitleResolver::getTitle()then we can do that separately in another issue.Comment #67
longwaveAlso backported the changes from more recent Symfony versions, which adds automatic FQCN aliases for invokable controllers, and the route aliases feature from Symfony 7.3: https://symfony.com/blog/new-in-symfony-7-3-routing-improvements#route-a...
Comment #68
longwaveAs suggested in #44 I think it would be trivial to add support for
_formalongside_controllerhere, we likely just have to scan theFormdirectory as well and check whether the class implementsFormInterface.Comment #69
godotislateSome comments on the MR, mostly small.
Do we need a follow up to add a filecache?
Comment #70
aaronmchaleFrom issue summary:
Might be worth noting that Drush provides a
core:routecommand which lists all routes, actually very useful sometimes! So given that we may conclude that we can do that in a follow-up, if we need it at all.While this is true, in a lot of cases the routing.yml file gives at best an incomplete picture, because most entity types will use a route provider, and so those routes won't be in the routing.yml file. So you could argue that this actually isn't really a strong advantage of the routing.yml file.
Comment #71
longwaveAddressed all MR feedback, this is ready for re-review. Also updated the deprecations and untagged as this didn't make 11.3.0; are the deprecations are niche enough that they can still be in 11.4 for removal in 12, or should we bump to 13?
Comment #72
catchWas a bit concerned about extra loading of PHP files for reflection during route rebuilds, but every time I make a typo in a controller class, I'm reminded that tends to happen during route rebuild already, so this might not result in much extra class loading at all. Haven't checked exactly where the controller classes get loaded from yet.
Comment #73
berdirI think that's because of \Drupal\Core\Entity\EntityResolverManager, see #3539922: Deprecate EntityResolverManager procedural controllers, but I think if we add this then the argument about performance there goes away, so that might be one more reason to just won't fix that issue.
Comment #75
godotislateOne small comment on the MR.
Some questions:
_formbe done here or in a follow up? Would the attribute on form classes be put on the class? Or thebuildFormmethod?Otherwise, I think this looks pretty close, but I'm not sure what other comments in this issue might be outstanding. (It's a long comment thread.)
Comment #76
joachim commented> Note: The route name is optional and will be generated based upon the class name.
How? Where does this happen? For routes which omit the name (and I think this is a bad idea), developers will need to understand how to get the route name.
Comment #77
longwaveAdded some more test coverage, but three properties don't appear to be used in core.
hostis checked in the route matcher (but I don't remember ever seeing it used),schemesis only used for URL generation (but not matching that I can see) andconditionis never checked at all. Should we drop support for some or all these here? We can always add them later.I think
_formshould be handled in a followup so we can decide the best DX - maybe it even needs a separate attribute.I also think we shouldn't bother with file cache for now, again we could do that in a followup if it turns out that we need it.
I'm in two minds about the auto name generation, while Symfony supports it does it makes sense for us? Is there actually a use case in Drupal? We could require it for now, and then make it optional later if we wanted to.
Comment #78
godotislateI have used
schemeson a project before where Drupal generated an ics-formatted response using field data from an Event content type in an "addToCalendar" controller method. IIRC, (it was a long time ago), setting the firstschemesvalue to "webcal" made it possible that clicking on the link would open the link in the device's default calendar app. Also, I'm not sure about droppinghost, butconditionprobably could be dropped.We need to know route names for alters and for menus, local tasks, and local actions. While not every route will have those, I think it's hard to be confident when creating a route that it will never be used in any of those contexts. Also, the automate route name generation does not result in something using the FQCN (in the MR, looks like
\is replaced with_), so the route names are still magic strings that need to be looked up, just with more difficulty now. Though per #70, drush provides acore:routecommand to list all routes, so that probably would help.Comment #79
longwaveI dropped support for
conditionbecause while you can set it, it does nothing (this was the case in YAML, but we can be more explicit here).I also made
namerequired for the method attribute (but it's still optional for class level). We can always add auto-naming later but I don't think it's very useful and the name is not discoverable.Comment #80
longwaveUpdated the CR with more examples.
Comment #81
joachim commented> Also, the automate route name generation does not result in something using the FQCN (in the MR, looks like \ is replaced with _), so the route names are still magic strings that need to be looked up, just with more difficulty now. Though per #70, drush provides a core:route command to list all routes, so that probably would help.
That would make it quite a bit harder to go from seeing something that affects a route, or uses a route, and finding the controller for that route. You'd need to reverse-engineer the FQCN from the route name, instead of just grepping the codebase for the string.
Comment #82
godotislateAdded test coverage for the additional properties looks good.
Additional examples in https://www.drupal.org/node/3324758 look good as well. I also updated the version numbers in the other 2 CRs.
The change to require the route name also looks good.
I think there was a previous concern about POTX picking up the title, but seems like it was addressed in #66.
Comment #83
benjifisherThis issue is already RTBC, but it looks to me as though the issue summary could use some love:
Also from the issue summary:
I believe that
drush routealready handles dynamic routes. It can list all routes or details about a specific route, but not all routes for a module. For example:Comment #84
longwaveThanks - updated the IS, I think the Symfony issues are solved, I made a note of the API changes, and we don't need a release note for this as it doesn't affect site owners.
Comment #85
godotislateRe #83 - for this task, "Decide which of the Symfony route features to keep in - env, localized_paths, priority, _locale, _canonical_route stuff... not sure what to do with this", the MR supports these arguments from the Symfony
Routeattribute contructor:path,name,requirements,options,defaults,host,methods,schemes,priority, andalias.Of the remaining arguments, exceptions are thrown if
_localeorconditionis set. The others (format,utf8,stateless, andenv) are just ignored. Maybe exceptions should be thrown for those as well?_canonical_routeisn't an argument toRoute, and since there's no existing support in Drupal routing AFAICT fordefaults['_canonical_route'], it can continue to be ignored?Comment #86
catchOverall this looks great, couple of minor comments on the MR, and do we want to address #85?
Also I don't see a follow-up for converting core routes, but I would think we'd want to do probably all of them?
Comment #87
joachim commentedThe CR needs to say that controllers need to be in the Controller namespace.
Comment #88
godotislateMade the CR change.
Comment #89
godotislateAlso, I did a quick skim of comments, and I think these are follow ups that have been mentioned to be created:
Comment #90
joachim commentedIs it also the case that translated strings need to be translated explicitly in the attribute, unlike in routes where it's implicitly done for certain properties?
Comment #91
joachim commented> Other attributes will be merged together
Deep merge of arrays? Who wins if a clash? CR should say. And is this documented in the code?
Comment #92
catchWhether or not we add file cache support should probably depend on whether the controller classes get loaded for other reasons, and if we think it's possible to remove that too - if we do then it might be worth doing. Although there's also the issue that most router rebuilds happen on the cli, it's directly rebuilt when you call drush cr (as opposed to most other caches which get built on the next request that needs them). Which goes back to #3486503: Add a persistent cache for file-based discovery based on FileCache and having a file cache implementation that doesn't use apcu. All reasons not to do it here but worth a follow-up definitely.
Comment #93
longwaveAddressed MR feedback, also added an explanation of attribute merging to the CR.
@joachim plain text titles will still be passed through TranslatableMarkup, but they will not be picked up by potx, so best practice is to use TranslatableMarkup as we do elsewhere with translatable strings in attributes.
Comment #94
longwaveWe could also have followup(s) for this point from #58:
Once we've added this feature I think we could also add attributes in place of
module.links.*.yml?Comment #95
godotislateTwo small suggestions and I think we're there.
Comment #96
longwaveAccepted both suggestions, thanks!
Comment #97
godotislateThink it looks good.
Comment #98
godotislateNW for merge conflict.
Comment #99
longwaveCollided with #3579890: Convert expectation-less test mocks to stubs - Database, Routing, rebased, back to RTBC.
Comment #101
longwaveOpened a second MR for 11.x as the issue in #99 means we have diverged a bit.
Comment #102
catchNeeds a rebase/update for phpunit 12:
I already reviewed this before and it's had an even more recent review from @godotislate so happy to commit this as soon as it's back to RTBC.
Would also be good to open the follow-ups - e.g. attributes for links/tasks/actions all got discussed here and we should make sure we have issues for them so those ideas don't get lost.
Also one to evaluate performance once we've done lots of conversions, investigate file cache (but might need a non-apcu backend due to drush) etc.
Comment #103
godotislateFixed the deprecation messages on the main MR, but got these now:
Comment #104
longwaveAdded
#[AllowMockObjectsWithoutExpectations]to the deprecation test, it seems the most pragmatic way to solve it.Also rebased the 11.x branch.
Comment #105
longwaveSelf re-RTBCing, this has only had minor test fixes.
Comment #106
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #107
nod_I guess I need to make the bot check the MR with the target branch the same as the issue.
Comment #108
longwaveThe main branch MR did have a merge conflict, resolved and rebased.
Comment #112
catchCommitted/pushed to main and 11.x, thanks!
Comment #115
godotislateI think there was a cross-post with the bot, so setting back to Fixed per #112.
Stubbed some follow ups, hopefully didn't miss any:
Comment #117
aaronmchaleFantastic to see this get in. Another potential follow-up, use attributes to define services. Thoughts?
Comment #118
catch@aaronmchale that's already possible with autowiring. We're using it for several newish services in core but haven't gone back to convert everything else yet. Not sure where the meta is for that.
Similar for plugins.
@godotislate I think we probably want an issue to evaluate whether to use file cache for this or not too.
Comment #119
godotislate@aaronmchale: for service autodiscovery, there's #3422359: Directory based automatic service creation (inactive for 2 years), but I think a major issue with autodiscovery is the performance hit on container rebuild for iterating through entire directory trees and reflecting/loading into memory every file/class.
@catch: opened #3584878: [PP-2] Look into adding a file cache for route discovery.
Comment #120
aaronmchaleThanks both, that's useful to know.
@catch do you have an example to hand of one of those new services I could look at?
Comment #121
catch@aaronmchale so e.g.
but the YAML just looks like:
Or an even shorter one:
vs the constructor.
#3376163: Support #[AsEventListener] attribute on classes and methods is open to register event listeners via attributes, without any services.yml entry at all.
However what I think you actually asked for is #3422359: Directory based automatic service creation which @godotislate mentioned already.
Comment #122
stborchertUnfortunately not. You will still need to register the class making use of
#[AsEventListener]as service. Otherwise Symfony couldn't find it.Comment #123
joachim commentedSo to confirm, unlike in a YAML file where title strings are assumed to require translation, in an attribute you have to explicitly translate?
That should be pointed out in the CR if that's the case.
Comment #124
joachim commentedCR updated.
Comment #125
godotislateAdded #3586832: Allow kernel test classes to define their own routes as another follow up.