How do I make sure `context` is carried into the actions performed by `manage_relationship`?

Hi!

I was under the impression that Ash’s context “travels” along when managing relationships within actions. It seems not to be the case, is there a way to achieve this?

Here’s a simplified example of what I’m trying to do:

Let’s say we have Parent and Child resources. Parent has an update action refresh, and Child has the same action. We want any refresh on a Child to trigger the refresh action on the Parent. And we want the context provided to a Child’s action to be visible in the Parent’s action called via manage_relationship.

So I’ve tried something like this:


Ash.update!(a_child, %{some: :param}, action: :refresh, context: %{very: %{large: "map"}})
# ...

defmodule Child do
  use Ash.Resource # etc

  actions
    update :refresh do
      # arguments etc...
      change Child.Changes.Refresh
    end
  end
end

defmodule Parent do
  use Ash.Resource # etc

  actions
    update :refresh do
      # arguments etc...
      change Parent.Changes.Refresh
    end
  end
end

defmodule Child.Changes.Refresh do
  use Ash.Resource.Change
  import Ash.Changeset

  @impl true
  def change(changeset, _opts, _context) do
    dbg(changeset.context) # the pushed context is here

    payload = %{document_id: get_attribute(changeset, :parent_id)}

    manage_relationship(:parent, payload, on_match: {:update, :refresh})
  end
end

defmodule Parent.Changes.Refresh do
  use Ash.Resource.Change
  import Ash.Changeset

  @impl true
  def change(changeset, _opts, _context) do
    dbg(changeset.context) # the pushed context is not here...

    # do something else with
    changeset
  end
end

I guess I could solve this but adding this context as an argument to Parent’s refresh action. But am I missing something, is context not as “global” as I expected?

Thanks in advance for taking a look at this.

Correct, context is not global. The primary reason is because all kinds of things can be stored in context, including things like context: %{data_layer: "table"}, naturally wouldn’t want to pass that around.

If you need to pass data down you either need to alter the payload that you pass in or call the child actions yourself.

I’m also open to adding a special key in context[:private] that passes context to any child managed relationships etc, or an option to manage_relationship/3 that does it, that kind of thing.

Thanks, that makes sense. Even though my expectation was different, I think it’s a good default.

I would not mind having an option to pass context through manage_relationship (default being the current behavior) :wink:. But I can work around my problem in other ways in the meantime.

2 Likes

An option to manage_relationship would be sweeet!

I likely won’t be able to do it any time soon unfortunately, but someone should open an issue to track it :smiley: It would likely be a good first contribution as well!