Conversation
|
It adds on suggestions from #966
Other victor suggestions (#966 (review))
my interpretation is the array parameter, for example:
If we ever have newer interfaces/classes, these events should be updated. Ideally, I think a scenario where BugData is a readonly class, and we have a class for Bug-Modification, which is oriented to being aware of the delta changes, providing more control for notifications, and work in coordination with other entities (bugnotes, attachments, etc). That's another story, though. At the moment, out only interface is BugData
ideally, execution could be stopped with an error. In a scenario where we supports raising error as exceptions, then that could be catched and managed.
in this PR, notifications are kept as they were, triggered in the callers. BugData already have some logic that triggers notifications, so that could be used. However that was not changed in order to keep this PR more simple. |
vboctor
left a comment
There was a problem hiding this comment.
I added some inline comments. Didn't test or review in full details that all actions that need to run continue to run. Though with exception of one case (from memory) it looked good, the one case could be right though.
High level comments:
- The way we call APIs by name and array of args is not IDE friendly. They won't be detected anymore by the IDE as usages.
- The lack of event specific objects makes event handling more complex and complete to detect the delta. The plugin has to do a diff to reverse engineer the change type. The plugins will also be blind to changes that happen only as part of the callbacks. We should really try not to have bug modifying logic in the callbacks. For example: it is OK to send an email, update history, but we shouldn't set custom fields, add notes, add attachments, etc.
- I'm not sure that changes by plugins will trigger the appropriate history changes. I suspect that we only track changes based on the original change.
Imagine the approach of having an object for change status. Such change can include all information that about the change including old status, new status, any extra fields set, and note added. This provides the plugin with the ability to validate and handle the event as a bundle with full visibility.
|
|
||
| $t_old_data = bug_get( $this->id, true ); | ||
|
|
||
| event_signal( 'EVENT_BUG_UPDATE_PRE', array( array( 'modified_bug' => $this, 'original_bug' => $t_old_data ) ) ); |
There was a problem hiding this comment.
I would rather use old and new and avoid the term bug.
| call_user_func_array( $t_callback['func'], $t_callback['params'] ); | ||
| } | ||
|
|
||
| event_signal( 'EVENT_BUG_UPDATE_POST', array( array( 'modified_bug' => $this, 'original_bug' => $t_old_data ) ) ); |
| $t_bugnote_id = bugnote_add( $p_bug_id, $p_bugnote_text, $p_time_tracking, $p_bugnote_private, 0, '', null, false ); | ||
| bugnote_process_mentions( $p_bug_id, $t_bugnote_id, $p_bugnote_text ); | ||
| # @TODO | ||
| # If time tracking is enabled, this code is executed outside of BugData validation. |
There was a problem hiding this comment.
I don't recall the reason why we do this special behavior for time tracking notes? Do you know why?
Btw, @ TODOs are picked up by some IDEs like PhpStorm, but they show the first line next to them, so it is generally good practice to try the have the todo on the same line and having enough info on first line to provide an idea of what needs to be done. There are multiple occurrences of such usage of @ TODOs.
| if( user_exists( $t_old_reporter_id ) ) { | ||
| bug_monitor( $p_duplicate_id, $t_old_reporter_id ); | ||
| if( user_exists( $t_bugdata->reporter_id ) ) { | ||
| $t_bugdata->add_update_callback( 'bug_monitor', array( $p_duplicate_id, $t_bugdata->reporter_id ) ); |
There was a problem hiding this comment.
This is a general comment about the approach for registering callbacks. Since this is no longer a normal call to APIs like bug_monitor(), IDEs like PhpStorm won't pickup such usage as a call to the function. Should we instead have a callback function that would call such APIs normally?
| </para> | ||
|
|
||
| <para> | ||
| Note: As the create action may be altered or stopped by other plugins, |
There was a problem hiding this comment.
How should plugins trigger the stopping in a way that works well with web UI and APIs? Is it via trigger_error? It is worth testing this case and clarifying guidance in docs as needed.
BugData::update() always calls history_log_event_direct for its properties. Any change should be logged to history.
I see that would mean use anonymous functions, like they are used in some places in this PR by assigning a variable:
yes, trigger_error is currently the only way to stop an invalid bug change. It's definitely not oriented to "works well with web UI and APIs", but we don't have any other way at the moment.
That's the same behaviour we have now (for BugData properties).
modifying in the callbacks was precisely the motivation to add them.
Additionally. |
Good.
Yes
Would it be too hard to support exceptions thrown from event handlers? i.e. for event X, plugins can throw exception Y and it will be caught by the
To summarize my position here:
I was just suggesting changing the naming of the old and new objects rather than changing how you send them to the events.
I understand. However, we still have the gap where plugins don't see a note change. And have to manually stitch a status change + related note. For example, there is no way for a plugin to stop changing a status without a note explaining the work or making sure that a custom field is populated with some value. If we use event objects, then they can look at the whole change, approve it or modify it, then it goes through. |
In some other place i suggested introducing an alternate error handler that packages the captured error in an exception, and then throws it. The context was to manage bug-action-group where one error would not stop the whole batch. This would be another application of the same concept.
I dont know. For sure: we are lacking full coverage with the current events. Almost any action that doesn't come from bug-update-page or bug-create-page is not managed by those.
ok, but i'd rather be sure that this is a desired trait for future events too. This array thing makes more sense when an event would have a bigger number of parameters. So the plugin callee should not define: If you have other plans, of which there are many possibilities, there's the option to leave the array parameter out, to keep consistency with the old event convention, until a better defined methodology is defined. |
I think it is a common pattern for event handlers to get an event type + event args. And the args is either a strongly typed class or a property bag with keys and values. Events can start with single or two parameters, but then in the future, more may be added to provide more context or whatever, and we need a way to do them. When referring to an array/property bag, keys are much better than array offsets. Here is an example for triggering
It is unclear to me if we are benefiting from having callbacks vs. running such follow up tasks after the call to ->update() or ->create()? It seems that we update db, email, then call callbacks anyway. So does it make a different if it is inside the update/create calls vs. called after from the calling code? I get the value though in the case where the code needs to run after saving to validating via event, validating via core, saving to db and before email notifications. So my feel is that they can be useful when we need such callbacks to be called in the middle of the create/update functions. And maybe we overtime just reduce reliance on follow up bug updates to BugData in such functions. If code can just run after update()/create() is done, then it shouldn't be a callback. If code needs to run in the middle of these methods, then it should be but we should make it IDE friendly. |
6de40fd to
f330874
Compare
|
Updated with some of the requested changes
|
|
@vboctor reminder! |
A callback queue in BugData object. The registered callbacks are performed in the update() function, after the data are saved, and before the EVENT_UPDATE_BUG event is called. These callbacks are needed becasuse with some bug actions, some auxiliary operations must be performed between the events EVENT_UPDATE_BUG_DATA and EVENT_UPDATE_BUG. For example: bug notes, must be inserted only if the action is performed, which can be stopped if the validation fails.
EVENT_UPDATE_BUG_DATA and EVENT_UPDATE_BUG are now triggered in the BugData update function. Rewrite bug_update.php to reflect this change.
Use BugData class for bug modifications
Use BugData class for bug modifications
Use BugData class for bug modifications
Use BugData class for bug modifications
Use BugData class for bug modifications
Use BugData class for bug modifications
Use BugData class for bug modifications
Use BugData class for bug modifications
Use BugData class for bug modifications
Avoid early returns in update method to make sure all update steps are performed.
Use the returned BugData object from the event call to comply with the
definition of EVENT_UPDATE_BUG_DATA as EVENT_TYPE_EXECUTE
The argumentation, as explained in the pull request:
>>>
Currently we have EVENT_UPDATE_BUG_DATA defined as "chain". This means
that the returned value by the event call is the object expected to be
used.
In the case of modifying a BugData object, this is not needed, as the
BugData object passed as parameter to the event is a reference, and
changes made through this reference are made into the actual (original)
object.
Let's see the BugData::update() function after this PR:
inside update(), we call:
event_signal( 'EVENT_UPDATE_BUG_DATA', $this, $t_old_data );
and later, we update the bug with the values from $this.
If the plugin makes changes to the supplied BugData reference, there is
no problem, they are saved in the actual object.
But, being a chain event, the returned reference may be altered, to
another object not being the original one. This doesnt make much sense,
but is possible though. For example:
function plugin_bug_data_hook( $event, $updated_bug, $old_bug ){
$another_data = new BugData();
...
return $another_data;
}
back to the BugData::update(), we cannot update "self" with another
reference, from inside the object.
<<<
The described exceptions are corner cases that probably should not be
supported, but as current event functionality allows it, the changes in
this commit are made to account for it.
The call to validate() is now placed after the event call, which may return a modified object. The validation is now placed immediatly before database update.
EVENT_REPORT_BUG_DATA and EVENT_REPORT_BUG are now triggered in the BugData create function.
Move the code that is executed after bug creation into callbacks, to be campleted before EVENT_REPORT_BUG is event is triggered
Create new events for changes triggered from BugData internal functions. 'EVENT_API_REPORT_BUG_PRE' => EVENT_TYPE_EXECUTE, 'EVENT_API_REPORT_BUG_POST' => EVENT_TYPE_EXECUTE, 'EVENT_API_UPDATE_BUG_PRE' => EVENT_TYPE_EXECUTE, 'EVENT_API_UPDATE_BUG_POST' => EVENT_TYPE_EXECUTE, Revert the changes from: 3c7f573 As with the new events they are not needed.
As new events have been created, keep the old ones to keep old behaviour for compatibility.
At this point we dont know the bug id, so use the call without it.
Rename the new events after suggestions from Pull Request
Make both events: EVENT_BUG_UPDATE_PRE, EVENT_BUG_UPDATE_POST have the same parameter definition.
Function add_create_callback() as wrapper for add_update_callback(), named in context of bug creation.
Replace callbacks tha use call_user_func_array with anonymous functions taht make the actual function call. This way IDE tools can detenct function usages.
Use shorter terms 'new' and 'old', omitting the term 'bug'.
If timetracking is enabled, the configuration may be set to not allow a timetracking note with empty text. Do a previous check before doing the actual bug update. If the bugnote is not valid, an error would stop the execution before actual changes are made.
f330874 to
e9caf0d
Compare
|
@vboctor |
|
@cproensa will have a look again. Would be good to get others to have a look too. |
|
@vboctor Could you at this point of the command refactoring, provide a solution for processing events consistently? |
|
@cproensa I'm working on more commands like |
How is that? You can introduce new events, or trigger the existing ones, for each "command", but that won't solve the same issue that i try to address here. |
|
outdated |

This replaces PR #966
rebased to current master