Defining `get_by` on a code interface doesn't do type-checking of the argument
- Dominant language
- Elixir
- Stars
- 2.5k
- Forks
- 422
- Avg merge
- 23h 26m
- Merged PRs (30d)
- 46
Description
**Describe the bug**
A code interface to get a single record by UUID primary key:
```elixir
define :get_artist_by_id, action: :read, get_by: :id
```
Calling the generated function with an invalid UUID raises an error from within Ecto:
```
iex(8)> Tunez.Music.get_artist_by_id("not a uuid")
{:error,
%Ash.Error.Invalid{
bread_crumbs: ["Error returned from: Tunez.Music.Artist.read"],
query: "#Query<>",
errors: [
%Ash.Error.Query.InvalidFilterValue{
message: nil,
value: "not a uuid",
context: #Ecto.Query,
splode: Ash.Error,
bread_crumbs: ["Error returned from: Tunez.Music.Artist.read"],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :invalid
}
]
}}
```
Whereas if the code interface used a real action that applied a `get_by` filter:
```elixir
# in the domain
define :get_artist_by_id, action: :get_by_id, args: [:id]
# in the resource
actions do
read :get_by_id do
get_by :id
end
end
```
Calling the function would properly validate the UUID before running the action:
```
iex(5)> Tunez.Music.get_artist_by_id("not a uuid")
{:error,
%Ash.Error.Invalid{
errors: [
%Ash.Error.Query.InvalidArgument{
field: :id,
message: "is invalid",
value: "not a uuid",
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :invalid
}
]
}}
```
I expected these two function definitions to be exactly equivalent, but that is not the case. The `get_by` code interface version skips past the UUID type check, and the non-UUID value gets all the way into the Ecto query.
**Runtime**
- Elixir version 1.18.2-otp-27
- Erlang version 27.2.1
- OS macOS Sequoia
- Ash version 3.5.4
Contributor guide
Research direction
Start by reproducing the two code-interface definitions shown in the issue and compare their argument handling for an invalid UUID. Trace the `get_by` code-interface path and the real action path to find where validation differs. Done means both forms reject the invalid value as an `InvalidArgument` before it reaches the Ecto query.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- elixir
- Domain
- backend-api-design
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100