I think I must be misunderstanding what code_interface optionals are, and how I can use optional parameters in an action.
Based on the queries that are being generated, this just isn’t right:
actions do
read :for_sprint do
argument :sprint_id, :uuid, allow_nil?: false
argument :type, :atom, allow_nil?: true
case arg(:type) do
nil -> filter expr(sprint_id == ^arg(:sprint_id) and type in COE.Types.ActivityTypes.values)
t -> filter expr(sprint_id == ^arg(:sprint_id) and type == ^t)
end
end
code_interface do
define_for COE.Walk
define :for_sprint, args: [:sprint_id, {:optional, :type}]
end
Which creates the following query – seems like the type parameter is not being evaluated in the case statement…?
query: #Ash.Query<
resource: COE.Walk.Activity,
arguments: %{type: nil, sprint_id: "c103f804-043b-40e5-9368-55c3f9992280"},
filter: #Ash.Filter<sprint_id == "c103f804-043b-40e5-9368-55c3f9992280" and type == nil>,
You would need to do this as a preparation. The way you are doing it now is evaluating it at compile time.
read :for_sprint do
argument :sprint_id, :uuid, allow_nil?: false
argument :type, :atom, allow_nil?: true
prepare fn query, _ ->
sprint_id = Ash.Query.get_argument(query, :sprint_id)
case Ash.Query.get_argument(query, :type) do
nil -> Ash.Query.filter(sprint_id == ^sprint_id and type in COE.Types.ActivityTypes.values)
t -> Ash.Query.filter(sprint_id == ^sprint_id and type == ^t)
end
end
end
I think @barnabasJ’s answer is definitely the clearer way to write this, but I do want to point out that expressions are “partially evaluated” before being executed in the data layer or in elixir, which allows for this to be equivalent to the functional preparation version. Use the function, as its easier to understand in general, but this property of Ash expressions comes in handy sometimes.
read :for_sprint do
...
filter expr(
if is_nil(^arg(:type)) do
sprint_id == ^arg(:sprint_id) and type in ^COE.Types.ActivityTypes.values()
else
sprint_id == ^arg(:sprint_id) and type == ^arg(:type)
end
)
end