Validation that tests relationship value (without using custom validator?)

Thanks for this @zachdaniel, that was hugely helpful in furthering my understanding of atomics.

I did run headlong into:

%Ash.Error.Framework.CanNotBeAtomic{resource: COE.Walk.Activity, change: COE.Validations.ValidateWithinSprint, reason: "Create actions cannot be made atomic", splode: Ash.Error, bread_crumbs: [], vars: [], path: [], stacktrace: #Splode.Stacktrace<>, class: :framework}

Which I did not realize. I need to implement it as a non-atomic validate/3 function. (And presumably keep the atomic version for, e.g., updates).

The parallel writeup here was also a great read: Does validations in update actions always require you to load the fields the validations are working on - #5 by anagrius.

My first attempt was to try and load/2 the sprint inside the validate/3:

sprint = Ash.load!(changeset.data, [:sprint])

That failed. I’m assuming, because the activity is not yet persisted. But I was a bit surprised. The changeset does have the relationship. I would have thought load/2 would be smart enough but, my understanding is not complete.

So… I proceeded (hopefully correctly) with the following validate/3 function.

  def validate(changeset, _, _) do
    sprint = Sprint |> Ash.Query.filter(id == ^Ash.Changeset.get_attribute(changeset, :sprint_id)) |> Ash.read_one!()
    date = Ash.Changeset.get_attribute(changeset, :date)
    if date < sprint.start_date or date > sprint.end_date do
      {:error, field: :base, message: "activity date must be between the sprint's range of #{sprint.start_date} and #{sprint.end_date}"}
    else
      :ok
    end
  end

The only bit I’m a little uncomfortable with is the read_one call. The above works. I’m just wondering if it is less efficient than it could be, or if there’s a more idiomatic approach.