Problem/Motivation
Now that we have #3381551: Performance optimization: Unify the event and process initialization layer fixed, there is basically only one Drupal\eca\Event\ConditionalApplianceInterface::appliesForWildcard implementation required to determine whether a triggered event applies for a specific configured ECA process. The current design problem we still face here is, that this logic currently is only coverable by system events (Symfony events). But as we have realized, that logic is very ECA-specific and therefore it should rather belong into the according event plugin instead.
Steps to reproduce
Proposed resolution
For being able to let ECA event plugins cover the appliance aspect in the most efficient way possible, we need to extend the Processor service that it's able to identify the according event plugin implementation, and then let it invoke the according method for appliance on behalf of the plugin implementation. This requires API changes on the ECA event plugin.
Remaining tasks
User interface changes
API changes
Data model changes
Issue fork eca-3382070
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
mxh commentedThis might be a potential blocker for #3348422: Make all token data providers discoverable and expose them in the UI and in the ECA Guide because this change will introduce a general mechanic for event plugins being automatically included for conditional appliance and for being added as data provider.
Comment #4
mxh commentedThink I found a possible solution how to achieve the appliance behavior in the
Processorservice: The plugin manager for ECA events may provide a method for getting the according plugin ID of a system event. Then the processor can call a static method of the plugin's class, i.e. without the need to instantiate a plugin object on that early step. Created an MR390 that includes the approach.In that MR I already refactored the
eca_accesssubmodule's events so that their logic of being a data provider and for conditional appliance got completely moved into the according plugin implementation. I didn't already refactor all other sub-modules, as this currently exceeds my time capacities.For the intermediate phase, where it may be possible that either the system event or the plugin event implements being a data provider and/or conditional appliance, I decided to support both scenarios in this MR. It might make sense to support both scenarios even until 3.0.x, because it's a huge effort to refactor all existing implementations. If we'd want to enforce changing everything, then we'd also need to enforce other integrations and for that we'd need to remove the according interfaces entirely or move them into the plugin namespace.
When refactoring: For very different logics of data providers and conditional appliances, it might make sense for a plugin implementation to use separate child event plugin classes for it, instead of bloating up one plugin class with different cases. For such a situation, I think it's possible that you can manually define a
'class'entry within each entry of an implementation ofDrupal\eca\Plugin\ECA\Event\EventInterface::definitionswhere you can point to a specific FQCN of a child class. I was already considering such a way in theeca_accessrefactoring example, but I think for that module it's still fine so I left it in one plugin class.Comment #5
mxh commentedComment #7
jurgenhaasAs always, this is outstanding! All the event handling in ECA is coming along really nicely, and it's really worth the refactoring as not only future maintenance will become much less difficult, but also integration of new events will be much more straightforward.
Yes, I like that approach.
Looking good, shall I alter the other sub-modules then?
ECA 2.0 will bring even more breaking changes for the event plugins, both for system event and ECA event plugins. So, all the integrations will have to be adjusted already, so I'd prefer to remove the double support now. TBH, what most integrations provide, are conditions and actions, but not events that much. That said, I think the overall amount of extra work seems reasonable.
Yes, absolutely! Having a single plugin class combined with a deriver was an approach that allowed the EventSubscriber to get the static definition array without having to consult the event plugin manager. That's no longer needed, and therefore, implementing individual plugins will now be the preferred mechanic, I think. However, that can be done later as well, as it should not break anything and foremost doesn't introduce any API change.
I've committed a small code style change to the MR and also spotted a naming convention, that we haven't thought about before: I only noticed the
machine-nametoken in the\Drupal\eca\Plugin\ECA\Event\EventBase::buildEventDatamethod, which is really nice. However, we should consider using underscores instead of hyphens in token names. We've already updated that forcurrent_user,current_formand others in the past, but we have more of those, that we should probably change. I know, these names got introduced before, I only noticed it now in the exposed location. And that made me realize that even more of those inconsistencies are around. But we should address this in a separate issue.Comment #8
jurgenhaasComment #9
mxh commentedThanks for looking into this, glad you like that approach as well. I added a comment into the Gitlab issue fork regards your commit, which would be great if that could be resolved.
If you want to take this part, yes sure.
That's ok for me granted we've got all our sub-modules covered. I marked those double-supporting code blocks with a @todo comment in the MR and they can be removed then.
Agreed.
Thanks for creating a separate issue for that topic.
Comment #10
jurgenhaasI have committed in 2 steps: the first commit moves all the
ConditionalApplianceInterfaceimplementations and the second one moves theDataProviderInterfaceimplementations and removes the support of system events as data providers. Hope it is easier to review that way.As for
DataProviderInterfacewe do have a few implementations that are not ECA event related, e.g.\Drupal\eca_form\Token\CurrentFormDataProvider,\Drupal\eca_queue\Task,EcaExecutionEventDataSubscriberand a few in theDrupal\eca\Tokennamespace. I left them as they are, but WRT extracting provided tokens into the ECA Guide, we may have to find a way to discover all data providers.Also, some of the ECA events have to override
getDataand since the interface forces a return type of?DataTransferObject, I had to wrap all the returned data accordingly. Tests are still all green, but I'm not sure if that change may have some implication elsewhere?I also commented on your thread in the MR, happy to further discuss that one.
Comment #11
mxh commentedThe interface method
Drupal\eca\Token\DataProviderInterface::getDatais declared to returnmixed, because it is allowed to return basically anything, not only DTOs. The token system could take basically anything as data argument, and that's why we need to keep thatmixedreturn type.Edit: I see the problem now why you're using the return type
?DataTransferObjectinstead. It is because currentlyDrupal\eca\Plugin\ECA\Event\EventBase::getDatadeclares that return type. But actually that's too limiting (as of the interface), so instead we need to change the return type ofDrupal\eca\Plugin\ECA\Event\EventBase::getDatatomixed, so that child classes are able to make full use of the interface.Comment #12
mxh commentedWe need to evaluate, whether some of them can be moved into the plugins. If not, we could tag any service being a data provider (via services.yml), so that we can collect them into another service for our required discovery.
Comment #13
mxh commentedAs of the currently too limiting (and thus wrong) return type of
Drupal\eca\Plugin\ECA\Event\EventBase::getDataI set this to NW. We need to change the return type tomixed(same as the interface). Any adjustment (probably wrapping ohter data types with DTOs) that was made because of that return type limitation needs to be reverted.Comment #14
jurgenhaasI have now reverted the return types in
getDataoverrides and also updated the declaration in the base class. Also, resolved the thread and setting now back to NR.Comment #15
mxh commentedThat's great! Took a quick look and found following missing to do's:
Drupal\eca\Event\ConditionalApplianceInterface. For being able to do so, the only missing event that needs to be refactored isDrupal\eca_test_array\Event\ArrayWriteEvent.Drupal\eca\EventSubscriber\EcaExecutionAccountSubscriber,Drupal\eca\EventSubscriber\EcaExecutionEntitySubscriber,Drupal\eca\EventSubscriber\EcaExecutionFormSubscriberand some others where it would be better to have their logic covered by the according plugins instead. If we'd face different plugin implementations doing the same thing, maybe they could at least share a trait. I'm not sure whether that's to be addressed in #3348422: Make all token data providers discoverable and expose them in the UI and in the ECA Guide anyway, feel free to ignore this point if so.We probably have some data providers where it's not possible to provide a 100% clear definition / declarative layer, because their contents may vary by different context situations and user-defined configurations. The two where that's the case are
Drupal\eca_queue\TaskandDrupal\eca\Token\ContextDataProvider. But that is fine, we don't need 100% coverage or enforce all data providers being able to predict what kind of data they will provide. It's just important that we provide the mechanic for all data providers that can predict their types of provided data.Comment #16
mxh commentedSetting to NW for the (probably last) required to do, which is the removal of
Drupal\eca\Event\ConditionalApplianceInterface.As mentioned in #15 we have some event subscribers, that make use of interfaces. For example
Drupal\eca\EventSubscriber\EcaExecutionEntitySubscriberchecks forDrupal\eca\Event\EntityEventInterface. I think for being more consistent in this refactoring manner, it would also make more sense to move that logic into the according plugins instead, and then finally removing that used interface (in this exampleDrupal\eca\Event\EntityEventInterface). IMO this is not a must-have, especially because that type of interface is also being used elsewhere in other implementations. Similar goes for other such interfaces likeDrupal\eca\Event\FormEventInterfaceandDrupal\eca\Event\RenderEventInterface. Just mentioning here for sake of completeness. Maybe related to #3348422: Make all token data providers discoverable and expose them in the UI and in the ECA Guide anyway.Comment #17
jurgenhaasI've addressed the last remaining todo and also adjusted the event template for the code generator. So, this seems ready to go as I agree with #15 and #16 to address the left over DataProvider tasks in #3348422 and will update that issue accordingly.
Comment #18
mxh commentedGreat, nice work! Took a look into the MR and haven't found anyhing wrong. I couldn't check every changed imlementation in detail, as this is a huge change. But for that let's hope our existing tests are sufficiently covering.
Added a small commit that removes the event stack from the execution event subscriber, as it's not needed anymore.
Comment #20
jurgenhaasThat's great, thanks.
Merge this biggy, so we can now move on to the other steps. Feels great, this refactoring is even better than anticipated.