Closed (fixed)
Project:
State Machine
Version:
8.x-1.x-dev
Component:
Code
Priority:
Major
Category:
Bug report
Assigned:
Unassigned
Reporter:
Created:
5 May 2019 at 20:32 UTC
Updated:
26 Jul 2021 at 12:03 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
neograph734Comment #3
alauzon commentedHere is a patch for this issue. Can someone test it out for good and bad?
Comment #4
neograph734Thanks for the effort alauzon, I will give it a try soon.
Comment #5
neograph734applyTransitionById() in turn calls applyTransition(), but applyTransition() can be called directly too. I think the logic should be there instead; patch attached.
Comment #7
neograph734I think the StateItemTest test failed because during the test the state item value was overridden with a forbidden transition. Attached patch should override the value with the initial value again.
The other failing test are a result of #3058938: Tests are broken.
Comment #9
neograph734Comment #10
neograph734I have updated the test to actually assert that the right Exception is thrown.
Comment #11
neograph734Using expectException instead of the deprecated setExpectedException. (Did not notice due to a compatibility layer.)
Comment #12
bojanz commentedWhen I was building state_machine, the assumption was that guards would run/be checked before applyTransition() is called. That's one of the points of getAllowedTransitions(). So, applyTransition() feels like the wrong place for the check. But I'll let the future maintainers decide that.
My gut feeling says that this change is too big of a potential BC break to do safely in 1.x.
A possibility would be to introduce an optional parameter to applyTransition() which would perform the check.
Comment #13
neograph734Perhaps it is the definition of 'guard'. In my opinion a guard will do everything to stop a person from passing a certain point. Within that context I believe it makes sense to block everything that is not allowed.
That could a solution to prevent BC from breaking, but again I believe it would be better to have checking the default behavior. So I'd like to propose the opposite; have an optional parameter to check defaulted to TRUE, but still allowing people to set it to FALSE to proceed to that state without checking.
We'll see what the new maintainers think. Thanks for the reply :)
Comment #14
jsacksick commentedWell it's not only related to guards. Calling applyTransition() will apply the transition (i.e update the state).
Prior to #3083407: Fire intended events when invoking a specific transition the state was updated, but the event skipped (because the transition could not be found).
I also agree with @bojanz (comment #12), this is a too big BC break IMO...
I believe the main issue is code calling applyTransition, without actually checking whether the transition is allowed...
Comment #15
jsacksick commentedPerhaps, we should add an
isTransitionAllowed()method to StateItem, that'd make code that checks whether a transition is allowed before applying it cleaner.Update: there's already a
getTransitions()method which returns only allowed transitions.I understand the reasoning behind the patch proposed (and I like the approach since that'd ensure a transition that really shouldn't be applied isn't)...
But the BC concern is probably too important.
Comment #16
neograph734I believe there is already such a check in the workflow object (which is accessible from state)? I have seen it used somewhere.
Comment #17
jsacksick commentedStateItem already has a
getTransitions()method which returns only allowed transitions, so isAllowedTransition()shouldn't be needed.Comment #18
jsacksick commentedComing back to this... I actually think we should move forward with this as there's currently nothing preventing you from applying an invalid transition which is problematic and kind of defeat the purpose of this module IMO.
Because of this, there's currently no easy way to figure out/fix broken code applying transitions that are invalid.
Comment #19
jsacksick commentedIf we decide to go with this, the patch will need to be rerolled to use the
isTransitionAllowed()method introduced toStateItemin #3220114: Add a helper method for determining whether a state transition is allowed.Comment #20
neograph734Should be something like this. I've also tried to improve the exception message a bit. I felt it was too much implying that the current state was the cause, whereas a guard can block it on virtually everything.
Patch will fail, but should pass after you commit #3220114: Add a helper method for determining whether a state transition is allowed.
Comment #21
jsacksick commentedShould be $transition->getId() here.
Well the thing is, it could be either due to a guard, or because of the current state as well...
The problem is not just the guards here, but the fact that
applyTransition()literally doesn't check anything.Comment #22
jsacksick commentedCommitted the other patch, and it seems the tests need to be updated as the patch no longer applies;
Comment #23
neograph734Perhaps I should not be doing this after a day of work.. I should have seen that.
I got that, so the exception message is that the transition cannot be applied. The state is there for reference.
I'll have a look.
Comment #24
jsacksick commentedRerolled the patch.
Comment #25
jsacksick commentedThis is fixing a phpcs violation!
Comment #27
jsacksick commentedCommitted!
Comment #28
jsacksick commentedCreated a change record: https://www.drupal.org/node/3220507.
Comment #30
jonmcl commentedYou all broke my code! :(
Also, change record doesn't appear to exist.
My use case:
While I think it makes total sense to only allow valid transitions when a user is clicking a button, we have to apply some "hidden" transitions based on data processing jobs that run via cron. So our code advances the state when the processing job completes. The transition to the new state was guarded from users so that they didn't see options to use that transition. Now the cron processing job also can't use the transition.
Obviously there are workarounds, apply the state directly being the most obvious one, but then we lose transition events that we have subscribers for. What I'm doing temporarily is to have my own wrapper around applyTransition with it's own $force parameter. However, now I'm recreating much of the code behind applyTransition. I feel like it would have bene useful to, at the very least, implement a $force or $ignore_guards parameter to allow some sort of BC. I may be submitting a patch soon :)
Update: Possibly less of an issue that I thought. The well written StateItem class should still dispatch the event even if you set the state manually.