Skip to content

Commit 5e8f0b1

Browse files
authored
fix: extract params from routes for GET actions (#416)
fixes #349
1 parent fb82c10 commit 5e8f0b1

2 files changed

Lines changed: 121 additions & 2 deletions

File tree

lib/ash_json_api/request.ex

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,13 +1060,53 @@ defmodule AshJsonApi.Request do
10601060
String.to_existing_atom(string)
10611061
end
10621062

1063+
defp parse_query_params(
1064+
%{route: %{type: :route, method: method} = route, action: action} = request
1065+
)
1066+
when method in [:get, "GET"] do
1067+
path_param_names =
1068+
route.route
1069+
|> Path.split()
1070+
|> Enum.filter(&String.starts_with?(&1, ":"))
1071+
|> Enum.map(&String.trim_leading(&1, ":"))
1072+
1073+
action_arg_entries =
1074+
action.arguments
1075+
|> Enum.filter(& &1.public?)
1076+
|> Enum.reject(fn arg -> to_string(arg.name) in path_param_names end)
1077+
|> Enum.map(fn arg -> {arg.name, to_string(arg.name)} end)
1078+
1079+
route_param_entries =
1080+
route.query_params
1081+
|> List.wrap()
1082+
|> Enum.map(fn qp -> {qp, to_string(qp)} end)
1083+
1084+
effective_entries =
1085+
Enum.uniq_by(route_param_entries ++ action_arg_entries, fn {atom, _} -> atom end)
1086+
1087+
Enum.reduce(effective_entries, request, fn {atom_key, string_key}, request ->
1088+
case Map.fetch(request.query_params, string_key) do
1089+
{:ok, value} ->
1090+
args = request.arguments || %{}
1091+
%{request | arguments: Map.put(args, atom_key, value)}
1092+
1093+
:error ->
1094+
request
1095+
end
1096+
end)
1097+
end
1098+
10631099
defp parse_query_params(%{route: route} = request) do
10641100
route.query_params
10651101
|> List.wrap()
10661102
|> Enum.reduce(request, fn query_param, request ->
10671103
case Map.fetch(request.query_params, to_string(query_param)) do
1068-
{:ok, value} -> %{request | arguments: Map.put(request.arguments, query_param, value)}
1069-
:error -> request
1104+
{:ok, value} ->
1105+
args = request.arguments || %{}
1106+
%{request | arguments: Map.put(args, query_param, value)}
1107+
1108+
:error ->
1109+
request
10701110
end
10711111
end)
10721112
end

test/acceptance/generic_action_index_test.exs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,67 @@ defmodule Test.Acceptance.GenericActionIndexTest do
6565
end
6666
end
6767

68+
defmodule SearchResultImplicitQueryParams do
69+
use Ash.Resource,
70+
domain: Test.Acceptance.GenericActionIndexTest.Domain,
71+
extensions: [AshJsonApi.Resource]
72+
73+
resource do
74+
require_primary_key?(false)
75+
end
76+
77+
json_api do
78+
type("search_result_implicit")
79+
80+
routes do
81+
base("/search_implicit")
82+
# Intentionally no query_params: [...]
83+
route(:get, "/", :search)
84+
end
85+
end
86+
87+
actions do
88+
action :search, {:array, :struct} do
89+
constraints(items: [instance_of: __MODULE__])
90+
argument(:query, :string, allow_nil?: false)
91+
argument(:category, :string, allow_nil?: true)
92+
93+
run(fn input, _ ->
94+
query = input.arguments.query
95+
category = Map.get(input.arguments, :category)
96+
97+
results =
98+
case category do
99+
nil ->
100+
[
101+
%__MODULE__{title: "Result 1 for #{query}", content: "Content 1"},
102+
%__MODULE__{title: "Result 2 for #{query}", content: "Content 2"}
103+
]
104+
105+
category ->
106+
[
107+
%__MODULE__{
108+
title: "#{category} Result 1 for #{query}",
109+
content: "Category content 1"
110+
},
111+
%__MODULE__{
112+
title: "#{category} Result 2 for #{query}",
113+
content: "Category content 2"
114+
}
115+
]
116+
end
117+
118+
{:ok, results}
119+
end)
120+
end
121+
end
122+
123+
attributes do
124+
attribute(:title, :string, public?: true)
125+
attribute(:content, :string, public?: true)
126+
end
127+
end
128+
68129
defmodule Domain do
69130
use Ash.Domain,
70131
otp_app: :ash_json_api,
@@ -78,6 +139,7 @@ defmodule Test.Acceptance.GenericActionIndexTest do
78139

79140
resources do
80141
resource(SearchResult)
142+
resource(SearchResultImplicitQueryParams)
81143
end
82144
end
83145

@@ -151,6 +213,23 @@ defmodule Test.Acceptance.GenericActionIndexTest do
151213
"Expected source pointer '#{source_pointer}' to be /query"
152214
end
153215

216+
test "generic action index route accepts optional argument without query_params configured" do
217+
response =
218+
Domain
219+
|> get("/search_implicit?query=elixir&category=tutorial", status: 200)
220+
221+
assert response.resp_body == [
222+
%{
223+
"title" => "tutorial Result 1 for elixir",
224+
"content" => "Category content 1"
225+
},
226+
%{
227+
"title" => "tutorial Result 2 for elixir",
228+
"content" => "Category content 2"
229+
}
230+
]
231+
end
232+
154233
test "generic GET actions include arguments as query parameters in JSON schema" do
155234
schema = AshJsonApi.JsonSchema.generate([Domain])
156235

0 commit comments

Comments
 (0)