Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 13 additions & 31 deletions server/lib/orcasite/radio/candidate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ defmodule Orcasite.Radio.Candidate do
end
end

code_interface do
define :find_nearby_candidate
end

actions do
defaults [:read, :destroy]

Expand Down Expand Up @@ -145,38 +149,16 @@ defmodule Orcasite.Radio.Candidate do
allow_nil? false
end

argument :timestamp, :utc_datetime
argument :timestamp, :utc_datetime, allow_nil?: false
argument :within_minutes, :integer, default: 3
argument :feed_id, :string

prepare fn query, _context ->
require Ash.Query
timestamp = Ash.Query.get_argument(query, :timestamp)
within_minutes = Ash.Query.get_argument(query, :within_minutes)
category = Ash.Query.get_argument(query, :category)

feed_id =
Ash.Query.get_argument(query, :feed_id)
|> AshUUID.identify_format()
|> case do
:raw ->
Ash.Query.get_argument(query, :feed_id)

_ ->
"feed_" <> id = Ash.Query.get_argument(query, :feed_id)
{:ok, feed_id} = AshUUID.Encoder.decode(id)
feed_id
end

min_time = DateTime.add(timestamp, -within_minutes, :minute)
max_time = DateTime.add(timestamp, within_minutes, :minute)

query
|> Ash.Query.filter(
feed_id == ^feed_id and ^max_time >= min_time and max_time >= ^min_time and
category == ^category
)
end
argument :feed_id, :string, allow_nil?: false

filter expr(
feed_id == ^arg(:feed_id) and
category == ^arg(:category) and
min_time <= datetime_add(^arg(:timestamp), ^arg(:within_minutes), :minute) and
max_time >= datetime_add(^arg(:timestamp), -(^arg(:within_minutes)), :minute)
)
Comment on lines +154 to +161

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify type handling and caller updates for the refactored query.

The declarative filter approach is cleaner than manual preparation logic. However, please verify:

  1. Type compatibility: The feed_id argument is defined as :string (line 154), but the belongs_to :feed relationship (line 70) typically creates a UUID foreign key. Confirm that Ash properly converts the string argument to UUID when evaluating the filter expression at line 157.

  2. Caller updates: Since feed_id moved from internal logic to a required argument, ensure all call sites have been updated to pass this parameter.

  3. Filter behavior: The time overlap logic appears sound, but verify that the filter returns expected results, especially around edge cases at the exact within_minutes boundary.

Run the following script to find all usages of this action and verify they've been updated:


🏁 Script executed:

#!/bin/bash
# Find all calls to find_nearby_candidate to verify they pass feed_id

# Search for direct function calls
rg -n -C3 'find_nearby_candidate' --type=ex -g '!**/candidate.ex'

# Search for Ash.Query.for_read calls targeting this action
rg -n -C3 "for_read.*:find_nearby_candidate" --type=ex

# Search for any GraphQL queries that might use this action
rg -n -C3 "findNearbyCandidate" --type=ex --type=graphql

Length of output: 80


Let me fix the search command with proper file patterns:


🏁 Script executed:

#!/bin/bash
# Find all calls to find_nearby_candidate using correct file patterns

# Search for direct Elixir function calls to find_nearby_candidate
rg -n -C3 'find_nearby_candidate' -g '*.ex' -g '*.exs' -g '!**/candidate.ex'

# Also search for camelCase GraphQL naming
rg -n -C3 'findNearbyCandidate' -g '*.ex' -g '*.exs' -g '*.graphql'

Length of output: 746


Good! Found one caller. Let me verify the type of feed_id in the Detection resource and check for any other callers:


🏁 Script executed:

#!/bin/bash
# Find the Detection resource to check feed_id type
fd 'detection.ex' -g '*.ex' | head -5

# Also search more broadly for any other potential calls to find_nearby_candidate
rg -n 'find_nearby_candidate' -g '*.ex' -g '*.exs'

# Check for Ash.Query.for_read usage with find_nearby_candidate across the codebase
rg -n 'for_read.*find_nearby_candidate' -g '*.ex' -g '*.exs'

Length of output: 493



🏁 Script executed:

#!/bin/bash
# Find Detection resource file
find . -path ./node_modules -prune -o -name "*.ex" -type f -print | xargs grep -l "defmodule.*Detection" | grep -v candidate.ex | head -5

# Also check the candidate.ex file for the attribute_types definition
grep -n "attribute_types\|feed_id.*:id\|feed_id.*:uuid" server/lib/orcasite/radio/candidate.ex

Length of output: 350


🏁 Script executed:

#!/bin/bash
# Check the exact argument definition at line 154 in candidate.ex
sed -n '150,165p' server/lib/orcasite/radio/candidate.ex

# Check feed_id definition in detection.ex
grep -n "feed_id" server/lib/orcasite/radio/detection.ex | head -10

Length of output: 1013


🌐 Web query:

Ash framework UUID string type coercion arguments comparison

💡 Result:

Short answer — Ash's :uuid type accepts UUID string representations (and UUID values handled by the underlying type) for arguments and attributes; Ash will coerce input when possible via its UUID type and SQL queries generated by ash_postgres cast values to ::uuid for comparisons.

Details and practical notes:

  • Define arguments/attributes as :uuid (or {:array, :uuid} for lists). Passing a UUID string (e.g. "550e8400-e29b-41d4-a716-446655440000") is the usual way; Ash.Type.UUID handles casting. [1][2]
  • In actions/filters use arg(:id) or regular attribute comparison (e.g. filter expr(id == ^arg(:id))) — the generated SQL shows the value cast to ::uuid so comparisons are done as uuid DB type. [1][3]
  • If you expect complex shapes (maps) instead of plain UUID strings, use the appropriate type (e.g. :map) — supplying the wrong shape (map vs string) will yield an “invalid” validation error. Example threads show {:array, :uuid} expecting a list of UUID strings and failing when given maps. [1]
    References:
  • Ash resource/action examples (argument :id, :uuid; expr filter example). [1]
  • Ash types reference (Ash.Type.UUID). [2]
  • Example generated SQL showing ::uuid casts in comparisons. [3]

Change argument type from :string to :uuid for proper type compatibility.

Type mismatch found: The feed_id argument at line 154 is defined as :string, but the feed_id attribute (line 205) is an :id type (UUID). The caller in server/lib/orcasite/radio/detection/changes/update_candidate.ex:13 passes detection.feed_id, which is also a UUID. Change the argument definition to :uuid to align with Ash's type system and ensure proper SQL casting in the filter expression.

🤖 Prompt for AI Agents
In server/lib/orcasite/radio/candidate.ex around lines 154 to 161, the argument
:feed_id is currently defined as :string but must be :uuid to match the resource
attribute (id/UUID) and callers that pass UUIDs; change the argument type from
:string to :uuid (keeping allow_nil?: false) so Ash generates correct SQL
casting and the filter expression compares UUIDs correctly.

end

update :update do
Expand Down
Loading