Skip to content

Commit b443e23

Browse files
committed
feat: add helpers function to deal and merge headers and query
1 parent ec5984c commit b443e23

File tree

2 files changed

+175
-4
lines changed

2 files changed

+175
-4
lines changed

lib/supabase/fetcher.ex

+65-3
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ defmodule Supabase.Fetcher do
108108
url: Finch.Request.url(),
109109
options: Finch.request_opts(),
110110
service: Supabase.service(),
111-
query: map,
111+
query: list({String.t(), String.t()}),
112112
body_decoder: module,
113113
error_parser: module
114114
}
@@ -119,9 +119,9 @@ defmodule Supabase.Fetcher do
119119
:client,
120120
:body,
121121
method: :get,
122-
query: %{},
122+
query: [],
123123
options: [],
124-
headers: %{},
124+
headers: [],
125125
body_decoder: Supabase.Fetcher.JSONDecoder,
126126
error_parser: Supabase.ErrorParser
127127
]
@@ -505,6 +505,68 @@ defmodule Supabase.Fetcher do
505505
get_header(resp, header) || default
506506
end
507507

508+
@doc """
509+
Tries to find and return the value for a query param, given it name, if it doesn't
510+
existis, it returns the default value informed or `nil`.
511+
"""
512+
@spec get_query_param(t, param :: String.t(), default :: String.t() | nil) :: String.t() | nil
513+
def get_query_param(%__MODULE__{} = builder, key, default \\ nil)
514+
when is_binary(key) and (is_binary(default) or is_nil(default)) do
515+
case List.keyfind(builder.query, key, 0) do
516+
nil -> default
517+
{^key, value} -> value
518+
end
519+
end
520+
521+
@doc """
522+
Tries to find and return the value for a request headers, given it name, if it doesn't
523+
existis, it returns the default value informed or `nil`.
524+
525+
Do not confuse with `get_header/2`.
526+
"""
527+
@spec get_req_header(t, name :: String.t(), default :: String.t() | nil) :: String.t() | nil
528+
def get_req_header(%__MODULE__{} = builder, key, default \\ nil)
529+
when is_binary(key) and (is_binary(default) or is_nil(default)) do
530+
case List.keyfind(builder.headers, key, 0) do
531+
nil -> default
532+
{^key, value} -> value
533+
end
534+
end
535+
536+
@doc """
537+
Merges an existing query param value with a new one, prepending the new value
538+
with the existing one. If no current value exists for the param, this function
539+
will behave the same as `with_query/2`.
540+
"""
541+
@spec merge_query_param(t, param :: String.t(), value :: String.t(),
542+
with: joinner :: String.t()
543+
) :: t
544+
def merge_query_param(%__MODULE__{} = builder, key, value, [with: w] \\ [with: ","])
545+
when is_binary(key) and is_binary(value) and is_binary(w) do
546+
if curr = get_query_param(builder, key) do
547+
with_query(builder, %{key => Enum.join([curr, value], w)})
548+
else
549+
with_query(builder, %{key => value})
550+
end
551+
end
552+
553+
@doc """
554+
Merges an existing request header value with a new one, prepending the new value
555+
with the existing one. If no current value exists for the header, this function
556+
will behave the same as `with_headers/2`.
557+
"""
558+
@spec merge_req_header(t, header :: String.t(), value :: String.t(),
559+
with: joinner :: String.t()
560+
) :: t
561+
def merge_req_header(%__MODULE__{} = builder, key, value, [with: w] \\ [with: ","])
562+
when is_binary(key) and is_binary(value) and is_binary(w) do
563+
if curr = get_req_header(builder, key) do
564+
with_headers(builder, %{key => Enum.join([curr, value], w)})
565+
else
566+
with_headers(builder, %{key => value})
567+
end
568+
end
569+
508570
defimpl Inspect, for: Supabase.Fetcher do
509571
import Inspect.Algebra
510572

test/supabase/fetcher_test.exs

+110-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,116 @@ defmodule Supabase.FetcherTest do
165165
end
166166
end
167167

168-
# VCR and "integration" tests
168+
describe "get_query_param/3" do
169+
setup ctx do
170+
fetcher = ctx.client |> Fetcher.new() |> Fetcher.with_query(%{"key1" => "value1", "key2" => "value2"})
171+
{:ok, Map.put(ctx, :fetcher, fetcher)}
172+
end
173+
174+
test "retrieves an existing query parameter", %{fetcher: fetcher} do
175+
assert Fetcher.get_query_param(fetcher, "key1") == "value1"
176+
end
177+
178+
test "returns nil for a missing query parameter", %{fetcher: fetcher} do
179+
assert Fetcher.get_query_param(fetcher, "key3") == nil
180+
end
181+
182+
test "returns the default value for a missing query parameter", %{fetcher: fetcher} do
183+
assert Fetcher.get_query_param(fetcher, "key3", "default_value") == "default_value"
184+
end
185+
186+
test "handles empty query parameters gracefully", %{client: client} do
187+
fetcher = Fetcher.new(client)
188+
assert Fetcher.get_query_param(fetcher, "key") == nil
189+
end
190+
end
191+
192+
describe "get_req_header/3" do
193+
setup ctx do
194+
fetcher =
195+
Fetcher.new(ctx.client)
196+
|> Fetcher.with_headers(%{
197+
"Authorization" => "Bearer token",
198+
"Content-Type" => "application/json"
199+
})
200+
201+
{:ok, fetcher: fetcher}
202+
end
203+
204+
test "retrieves an existing header", %{fetcher: fetcher} do
205+
assert Fetcher.get_req_header(fetcher, "Authorization") == "Bearer token"
206+
end
207+
208+
test "returns nil for a missing header", %{fetcher: fetcher} do
209+
assert Fetcher.get_req_header(fetcher, "Accept") == nil
210+
end
211+
212+
test "returns the default value for a missing header", %{fetcher: fetcher} do
213+
assert Fetcher.get_req_header(fetcher, "Accept", "application/xml") == "application/xml"
214+
end
215+
216+
test "handles empty headers gracefully", %{client: client} do
217+
fetcher = Fetcher.new(client)
218+
assert Fetcher.get_req_header(fetcher, "Authorization") == nil
219+
end
220+
end
221+
222+
describe "merge_query_param/4" do
223+
setup ctx do
224+
fetcher = Fetcher.new(ctx.client) |> Fetcher.with_query(%{"key1" => "value1"})
225+
{:ok, fetcher: fetcher}
226+
end
227+
228+
test "merges a new query parameter when key does not exist", %{fetcher: fetcher} do
229+
updated_fetcher = Fetcher.merge_query_param(fetcher, "key2", "value2")
230+
assert Fetcher.get_query_param(updated_fetcher, "key2") == "value2"
231+
end
232+
233+
test "merges with default separator when key exists", %{fetcher: fetcher} do
234+
updated_fetcher = Fetcher.merge_query_param(fetcher, "key1", "value2")
235+
assert Fetcher.get_query_param(updated_fetcher, "key1") == "value1,value2"
236+
end
237+
238+
test "merges with custom separator when specified", %{fetcher: fetcher} do
239+
updated_fetcher = Fetcher.merge_query_param(fetcher, "key1", "value2", [with: "|"])
240+
assert Fetcher.get_query_param(updated_fetcher, "key1") == "value1|value2"
241+
end
242+
243+
test "handles merging into an empty query", %{client: client} do
244+
fetcher = Fetcher.new(client)
245+
updated_fetcher = Fetcher.merge_query_param(fetcher, "key1", "value1")
246+
assert Fetcher.get_query_param(updated_fetcher, "key1") == "value1"
247+
end
248+
end
249+
250+
describe "merge_req_header/4" do
251+
setup ctx do
252+
fetcher = Fetcher.new(ctx.client) |> Fetcher.with_headers(%{"key1" => "value1"})
253+
{:ok, fetcher: fetcher}
254+
end
255+
256+
test "merges a new header value when key does not exist", %{fetcher: fetcher} do
257+
updated_fetcher = Fetcher.merge_req_header(fetcher, "key2", "value2")
258+
assert Fetcher.get_req_header(updated_fetcher, "key2") == "value2"
259+
end
260+
261+
test "merges with default separator when key exists", %{fetcher: fetcher} do
262+
updated_fetcher = Fetcher.merge_req_header(fetcher, "key1", "value2")
263+
assert Fetcher.get_req_header(updated_fetcher, "key1") == "value1,value2"
264+
end
265+
266+
test "merges with custom separator when specified", %{fetcher: fetcher} do
267+
updated_fetcher = Fetcher.merge_req_header(fetcher, "key1", "value2", [with: "|"])
268+
assert Fetcher.get_req_header(updated_fetcher, "key1") == "value1|value2"
269+
end
270+
271+
test "handles merging into an empty header", %{client: client} do
272+
fetcher = Fetcher.new(client)
273+
updated_fetcher = Fetcher.merge_req_header(fetcher, "key1", "value1")
274+
assert Fetcher.get_req_header(updated_fetcher, "key1") == "value1"
275+
end
276+
end
277+
169278
defp have_header?(headers, name) do
170279
Enum.any?(headers, fn {k, _} ->
171280
String.downcase(k) == String.downcase(name)

0 commit comments

Comments
 (0)