Ash Framework Book - how to debug the actions

The step where previous_names are added is now preventing the save of an artist.
The artist table has the migrated field and the change function is in a separate module.
I see no messages in the terminal about why the save is not working.
How can i get some logging info from the submit action?
I added an inspect in the change function but nothing was output so i assume something is preventing it from executing this. I then added an input field for the previous_names field and started getting validation that it is required, even though allow_nil is true.

Any help appreciated.
Thanks

:wave: I might need to see how what your code looks like up to this point to say for sure. I believe there is a step in the book that addresses that.

To answer your question more directly, there are a few things you can do to debug an action/form.

With the form

With a form that has been returned in the {:error, form} case of AshPhoenix.Form, you can do

AshPhoenix.Form.errors(form, for_path: :all) |> IO.inspect()

Not all errors are displayed in the form automatically because they may not tie to fields that are editable in the form itself. We also log a warning (IIRC that is the level) when we encounter an error that could not be displayed to the user due to not implementing the AshPhoenix.FormData.Error protocol (a safety measure to avoid displaying internal details to the user.

You can also peek at form.source (or form.source.source, if you have a %Phoenix.Form{}

in the action itself

change fn changeset, _ -> 
  Ash.Changeset.after_transaction(changeset, fn result -> 
    IO.inspect(result)
    result
  end)
end

You can add a hook like this to display the ok/error result from the action.

You’ll want to make sure that your action is not accepting previous_names as an input, and that your change is being run in all necessary places to set the attribute, that it has a default of [] etc.

Thanks for your reply Zach. I inspected the result on save and go the following:

         errors: [
            %Ash.Error.Changes.InvalidChanges{
              fields: nil,
              message: "Change Tunez.Music.Changes.UpdatePreviousNames must be atomic, but could not be done atomically: Tunez.Music.Changes.UpdatePreviousNames does not implement `atomic/3`",

here is the update action:

    update :update do
      require_atomic? false
      accept [:name, :biography]
      change Tunez.Music.Changes.UpdatePreviousNames, where: [changing(:name)]
    end

I imagine the book should call this out, but the answer to that is to add require_atomic? false to the update action.

The action has require_atomic? false.

Hmm…now that is interesting :smile:

Would it be possible for you to push your code as you have it to Github? I can take a look :smile:

I noticed int he network tab that the app is falling back to longpoll, could this be a reason?

It needs to be change(changeset, opts, context), not change(changeset, context)

Also, I noticed you have duplicate migration files. That’s something that shouldn’t happen. You can not run mix ash.setup while there are duplicates. I assume you were experimenting with snapshots?

@ken-kost is correct about the def change/2 being the issue. This is an interesting one :sweat:

We have logic for an Ash.Resource.Change to automatically perform its atomic/3 callback if there is no change/3 callback defined. So when we try to do that, we find that there is no atomic/3 callback either, and that produces this error.

Something common to do with behaviours in Elixir (which Ash.Resource.Change is one), is to use @impl true or @impl BehaviourModule above each function that implements a callback of that behaviour. You can read more about that here: Typespecs reference — Elixir v1.18.3

If you had @impl true above def change(changeset, context), you would get a warning about that not being an actual callback available.

I think there are two things that we can do on our end to improve this:

@sevenseacat we can make sure to use @impl true in all of our examples of change/validation/calculation callback functions.

And for myself, I can add a check that at least one of change/3, atomic/3 or batch_change/3 is defined, as that is the minimum required for a change to function. That check also would have surfaced this issue :slight_smile:

1 Like

Can do! I always forget to put that in everywhere :sweat_smile:

3 Likes

And this error


will be in the next release of ash

2 Likes

Thanks for assisting me with this. I am new to Elixir and ash and will be using ash to build a multi tenant app that connects to an external api. Very impressed with ash so far.

1 Like

Now I’m thinking: what about having mix ash.gen.change? :thinking:

I’m here for it :+1:

and it’s here for you :call_me_hand:

1 Like

Apologies for the necro but I have some related nitpicky feedback and I specifically remembered this thread because the lack of @impl trues was causing me some dissonance as well. Perhaps you already caught this but since it’s not a change/validation/calculation I wanted to point it out: For the installed_extensions example in Speeding things up with custom database indexes, it would be helpful to include @impl true there as well.

Thanks, Rebecca and Zach!

1 Like

I’ve just added @impl true to all the generated functions in the Repo module after installing AshPostgres - installed_extensions/0, min_pg_version/0, and prefer_transaction?/0. Good call and thanks for that!

1 Like

Hello @sevenseacat , @zachdaniel ,

Can you please let me know if you have a best suggestion for implementing dual control and data filters in authorization?
In most enterprise applications you need maker/checker for sensitive actions/functions , also some users could be authorized to do an action like read/write on filtered set of data of the resource like certain branch or city ,…etc
Also if you can let me know what integrates well with Ash for record audit and maybe sensitive data view?

Thanks

For a maker/checker pattern that is something that I would likely use something like ash_state_machine (or just a :state attribute), with separate actions for making and checking, and separate policies on those two actions, allowing the maker to make and the checker to check etc. Could, for instance, just have a policy forbidding the maker from also being the checker etc.

For authorizing reads/writes on filtered sets of data, see policies. They are built for this purpose and will filter reads and writes appropriately.

For record audit, do you mean like audit logs/trails? If so we have ash_paper_trail as a core extension that will track changes to a resource over time.

For sensitive data view, I’m thinking you mean providing partial access to records etc., which can be implemented using field policies :slight_smile: