Trouble Understanding Reactor
I'm taking a stab at implementing a Reactor, but the documentation isn't very clear. I've read both the Reactor documentation and the AshReactor documentation for getting started, but I'm still getting tripped up. Here is a basic example:
From what I gather, this will forward the inputs defined inside the
create
to the default create
action of the User
resource. Am I right? How would this differ if I had to call a different action that wasn't default.
Next, I call this by placing an action on the User
resource like so:
Calling User.register_user
seems to run the RegisterUser
reactor, which runs the User.create
action like expected, but then I get this error:
Ok, so I then change my file to return just an :ok
to see if I can get it to work. It looks like this:
I run User.register_user
again and now I get this error (sorry for the formatting, this is how it's output):
POST IS TOO LONG FOR DISCORD, THE REST IS IN THE FIRST COMMENT35 Replies
Now it's saying that I need to return
{:ok, any}
, which I assume is what the call directly to the create
action was doing. I look at the documentation pages and I don't understand what I'm doing wrong. Can anyone point me in the right direction? Reactors seem very useful and there's multiple steps to the registration process, but I can't even seem to get it to run one.
well, maybe not
I'm actually not a wizkid at the bits and bobs of reactor
but your issue w/ the generic action is because the generic action doesn't have a return type
Yeah, that results in the first error where it says it only expects an
:ok
Is that
instance_of
what is expected as the return?Generic action inputs and return types are explicit
its the
:struct
type
which has an instance_of
constraint indicating what type of struct it isAh, I see now. Let me try it out really quick.
i.e
would be saying "it returns any struct"
vs
is saying "it returns a struct which is an instance of
__MODULE__
"Ah, yeah, adding that
:struct
at the top got it to work when creating a user. However, whenever there's an error returned during the create_user
call, say due to validation errors or a unique constraint on email, it returns a big error that wraps the Invalid
error, like so:
Does this mean if I'm using Reactors to handle multiple steps/actions done during the registration process, that I lose the ability to report validation errors back to the caller?🤔 ...that being an unknown error seems wrong to me
@jart is this something reactor is doing or something wrong w/ this specific reactor?
Reactor has it's own splodes but they don't interact nicely with Ash's ones. I wish they did.
🤔
why don't they?
probably because I didn't (don't) really get how they're supposed to work with ash
They don't have to be automagically cast back to fields necessarily, we can leave that to the user
but right now the setup is encoding the error as a string inside of an unknown error
which is effectively useless
It might be something wrong in splode
I think it might be because Reactor doesn't have all the error classes that Ash does
but we should add a test case for this and sort out how to get it to return an
Ash.Error.Invalid
containing reactors errors
it should only be a problem if it was the other way around
reactor having more classes than ash
but, we can figure out where to add some custom glue or some kind of adapter something or other to fix any mismatchyeah that makes sense. the current generic action -> reactor glue just maps up the run functions and nothing else
but maybe reactor just shouldn't wrap ash errors?
oh, I see what you're saying
but then reactor has to account for multiple errors being possible in an undo
not really
its always one error
just could be the grouping of multiple errors
it's one error that triggers the rollback, but undo's can collect errors from failed undos
oh, I see what you're saying
lemme just see what it looks like
splode should solve it if the error classes match
my vote is to just make sure that reactor has all the error classes of Ash, but even still, they both have
:invalid
so it shouldn't matter here.
where would I find the code for handling a step error in reactor?
anyway, some kind of protocol or adapter pattern would likely serve here in some wayall the errors live in Reactor.Error, but it will be StepRunner that actually records the errors
so in this code:
its playing nicely
look at step_runner.ex line 196
Thats something you could theoretically do something with
would be manual still like matching on a step error and returning it, but we could hide that away internally
I think its Ash's error class stuff doing this
Anyway, @Steve what you can do in the short term 🙂
(side note, I don't know how no one has mentioned this before, it seems like a really obvious thing, maybe people aren't using the automatic "run a reactor" option as much as we thought)
I didn't want to interrupt while y'all were talking. 😂 If I need something that interacts more nicely with Ash errors in the mean time, would I be able to do multi-step processes using just normal Ash? Is Reactor overkill for most multi-step processes?
https://hexdocs.pm/ash/multi-step-actions.html
Use reactor when: You need to compensate/undo changes across multiple external services Building complex workflows that require sophisticated error handling and rollback logic Coordinating long-running processes that span multiple systems
I'm looking through some of the more in-depth examples now, and it looks like they separate steps out into their own modules and return errors that way:
Yeah, taking it back to the basics I guess, why are you reaching for reactor 😄
I'm mostly experimenting right now. In this case, no particular reason other than to learn, which is why it was such a simple example. For registration, there's really only a few actions that need to happen after the user is created (creating some tokens, creating a couple new records for different resources, sending notifications).
I suppose I should start with figuring out how to do it the normal Ash way 😅
Gotcha, yeah your best bet is likely to use hooks
agreed. but also we need to make Reactor play nicer with Ash
etc.
the patterns around that are documented in the multi step actions guide that @jart linked to
All right, I'll play around with those and see if I can get it to work correctly. As always, I really appreciate the help!