-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add state_machines related events #3762
Conversation
@nirebu I think the main problem is that the names of the events don't make much sense. I'm thinking something like An alternative would be firing an The same considerations extend to shipments and payments, btw. |
What about |
@kennyadsl my idea was to pass the entire transition object, which responds to |
@aldesantis makes sense. I was also thinking to move the logic executed in the |
@kennyadsl we did not, but I agree — that makes sense. |
If we take this route, I'd also implement an order transition failure event with this block in a subscriber for consistency: after_failure do |order, transition|
order.logger.debug "Order #{order.number} halted transition on event #{transition.event} state #{transition.from}: #{order.errors.full_messages.join}"
end |
I like where this is going. Thanks @aldesantis and @kennyadsl for the suggestions!
@kennyadsl could you point me in the right direction on where to find such an example? I seem to able to find only |
I was thinking to order.logger.debug "Order #{order.number} transitioned from #{transition.from} to #{transition.to} via #{transition.event}" Let me know if it makes sense for you as well. |
Also, it may be worth exploring the idea of having events before transitions as well, with something like: base.state_machine do
before_transition do |order, transition|
Spree::Event.fire "order_before_#{transition.to_name}", order: order
end Regarding the granularity of event names:
I am not sure what would work better here. By having events such as By having a single |
Thanks @spaghetticode for adding to this, and also to have better worded what I was trying to put here in the meantime. By emitting the full transition event and making the user check at every transition if the code should react or not via a combination of I'd prefer to go down the road of Let me know what you think about this. |
81bd533
to
4d2f5e9
Compare
@nirebu is this ready for another review or are you still working here? |
@kennyadsl This Friday I've updated the branch with the longer event names and updated the first comment to explain them. If these are OK I can continue also with adding the relevant logging functions. |
I'm ok with the latest changes even if I can anticipate that the code will slightly change again. 😛 |
4d2f5e9
to
bf7ede5
Compare
@kennyadsl I've updated the PR content also with the |
@nirebu 👍 tbh I'd move them in a separate commit with a description that explains why we are adding them (consistency). Also, in my understanding, the failures should fire an event as well, isn't it? |
This adds an event emission for every success of the order, payment and shipment state_machines. It also brings the payment and shipment logging on par with the order one, which already logged its transition successes
This adds an event emission for every failure of the order, payment and shipment state_machines. It also adds transition failure logging to the payment and shipment state_machines for consistency with orders.
bf7ede5
to
15329d9
Compare
I've tried to compact the commit history separating the changes between success and failure events. And I also added the failure events with the following format: "order_failed_transition_to_#{transition.to}" I was also thinking whether it would be a good idea to move the logger calls to event subscribers or not. In my opinion they should stay in the state_machines definitions, but I'm also open to discussion on this one 😄 |
I'm also not completely sure about this one. We can move those actions to subscribers later with another PR if needed. |
order.logger.debug "Order #{order.number} transitioned from #{transition.from} to #{transition.to} via #{transition.event}" | ||
end | ||
|
||
after_failure do |order, transition| | ||
Spree::Event.fire "order_failed_transition_to_#{transition.to}", order: order |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about order_failed_transitioning_to_...
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could also work no problem, I have no strong opinions on transition
vs transitioning
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's wait for the opinion of @spaghetticode, which has more experience with events' names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I guess I am slightly partial to transitioned_to
, as it's a bit shorter.
In general, I think we should strive to make these event names descriptive and easily mappable to the state machine transition that triggered them. One extra bonus may be having names with consistent/predictable format, so if after_failure
spawns the event order_failed_transition_to_completed
, then for symmetry the name fo after_transition
may be changed to order_successful_transition_to_completed
. Unfortunately, it's a bit too long 🤷.
Maybe we can spend a little more time in searching the best names, also considering that changing them later will require a deprecation cycle.
How should we test events here? Using events as extension points means we need them to be stable and consistent so I want to make sure we've got good test coverage around how and when events are fired. |
@aldesantis I know you've been working with testing events in the past, do you have any advice for the legit @jarednorman comment about testing these events? |
Testing event emissionOur approach on client projects has always been fairly straightforward for testing event emission: stub_const('Spree::Event', class_spy(Spree::Event))
# run whatever code is supposed to fire the event
expect(Spree::Event).to have_received(:fire).with('...') An even better solution would be to have a "test" event adapter, similar to what ActiveJob provides. Testing subscriber behaviorAs for testing the actual subscribers, that's a bit trickier, because you need to make sure you're testing them in isolation — i.e., when you run the subscriber, you need to make sure there are no other subscribers that could interfere with your test. Our approach has been the following:
So our subscriber tests usually look like this: RSpec.describe MyEventSubscriber do
describe 'event_name' do
it 'does something' do
# set up the test here
# ...
perform_subscribers(only: described_class) do
Spree::Event.fire 'event_name'
end
# verify the side-effects of the subscriber here
# ...
end
end
end This pattern has worked very well for us so far in apps that rely very heavily on the event bus. I was already planning to contribute it upstream, so I can accelerate that if we all agree it's a good idea. 🙂 |
Thanks for your work on this @nirebu ❤️ The event system got a massive revamp, and it'll be available from Solidus v3.2 (at this point, it's only on master). All the event-driven architecture has been extracted to a standalone library: omnes. The new event system is much more powerful, and we aim to integrate all it provides on Solidus. For instance, events can be serialized to run handlers in the background. Because of that, we should move away from passing active record instances as the event payload. Besides, having the publication of the events so closely attached to the code in the state machine is not a good idea. Events should be handled outside the main database transaction, as they're a way to run code that is not coupled with the main flow (disclaimer: at this point, the state machine transactions don't run within a database transaction, but it should be considered as a flaw we need to fix). Because all of that, I think it's best to close this PR for now. Adding more events into |
Description
Ref #3684
This PR wants to address #3684 by adding an event emission for every transition change in the listed state machines.
I wanted to retain the events closer to the core as possible since every app built on Solidus could use such events with different semantic meaning. The strategy chosen for the event names is:
"order_transitioned_to_#{transition.to}"
Checklist: