ash icon indicating copy to clipboard operation
ash copied to clipboard

misleading error if arguments are forgotten in Ash.Query.for_read

Open frankdugan3 opened this issue 1 year ago • 1 comments

Describe the bug

When building a read query for an action with only optional arguments, a misleading error message occurs when no arguments are passed.

iex(1)> Ash.Query.for_read(Hsm.FFL.ADRecord, :bound_book_report, actor: nil)
** (Ash.Error.Forbidden) Forbidden Error

* The domain Hsm.FFL requires that an actor is provided at all times and none was provided.
  (elixir 1.17.3) lib/process.ex:864: Process.info/2
  (ash 3.4.35) 

To Reproduce

Build a read query on any action that has only optional arguments. Not sure if the same error also happens when the action has mandatory arguments.

Expected behavior

The error should indicate that it is an action that accepts arguments, but no arguments were passed. That or maybe it should automatically insert an empty map as the arguments? :thinking:

frankdugan3 avatar Oct 22 '24 18:10 frankdugan3

We should probably validate inputs first? Or require that inputs is a map? That second one would be a breaking change. Anyway, definitely confusing.

zachdaniel avatar Oct 22 '24 22:10 zachdaniel

I couldn't reproduce this. Simply running Ash.Query.for_read(resource, :action) returns an appropriate error:

iex(3)> Ash.Query.for_read(Ash.Test.QueryTest.User, :by_id)
#Ash.Query<
  resource: Ash.Test.QueryTest.User,
  filter: #Ash.Filter<id == nil>,
  errors: [
    %Ash.Error.Query.Required{
      field: :id,
      type: :argument,
      resource: Ash.Test.QueryTest.User,
      splode: Ash.Error,
      bread_crumbs: [],
      vars: [],
      path: [],
      stacktrace: #Splode.Stacktrace<>,
      class: :invalid
    }
  ]
>

Could it be that your domain force authorization, @frankdugan3? Because in that case the error looks exactly like you described (and it makes sense to me):

iex(5)> Ash.Query.for_read(Ash.Test.QueryTest.User, :by_id)
** (Ash.Error.Forbidden) Forbidden Error

* The domain Ash.Test.Domain requires that an actor is provided at all times and none was provided.

StephanH90 avatar Dec 16 '24 15:12 StephanH90

If a keyword list is passed then it should be treated as an opts list, so it should see actor: nil. the check to require an actor allows nil as a value, it just requires specifying it in some way.

zachdaniel avatar Dec 16 '24 16:12 zachdaniel

@frankdugan3 If you do Ash.Query.for_read(Hsm.FFL.ADRecord, :bound_book_report, %{}, actor: nil) should be fine.

I think the problem here is the last two args that have default, map and list. In your case actor: nil is args, not opts.

@zachdaniel Something like:

  def for_read(query, action_name, opts) when is_list(opts) do
    for_read(query, action_name, %{}, opts)
  end

could catch this case. wdyt?

ken-kost avatar Feb 01 '25 18:02 ken-kost

Yeah, that makes sense to me I think. 👌

zachdaniel avatar Feb 01 '25 18:02 zachdaniel

@frankdugan3 If you do Ash.Query.for_read(Hsm.FFL.ADRecord, :bound_book_report, %{}, actor: nil) should be fine.

To clarify, I understand how to work around this, the issue is about the error being misleading.

That aside, I think your solution of the guard clause on for_read is the correct fix.

frankdugan3 avatar Feb 01 '25 20:02 frankdugan3

@frankdugan3 If you do Ash.Query.for_read(Hsm.FFL.ADRecord, :bound_book_report, %{}, actor: nil) should be fine.

To clarify, I understand how to work around this, the issue is about the error being misleading.

That aside, I think your solution of the guard clause on for_read is the correct fix.

Yea, but it's a breaking change, so for version up.

ken-kost avatar Feb 01 '25 20:02 ken-kost

Unfortunately there's nothing we can do about this. In 4.0 we will make the inputs required for the lower level interfaces like this to solve this issue.

zachdaniel avatar Feb 03 '25 20:02 zachdaniel