Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add put_path_params_style/2 step #373

Merged
merged 5 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 9 additions & 0 deletions lib/req.ex
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@ defmodule Req do
* `:path_params` - if set, uses a templated request path (via
[`put_path_params`](`Req.Steps.put_path_params/1`) step.)

* `:path_params_style` - configures how `:path_params` are expressed (via
[`put_path_params`](`Req.Steps.put_path_params/1`) step). Can be one of:

* `:colon` - default, params are expressed as `:name` in the path.

* `:curly` - uses [OpenAPI](https://swagger.io/specification/)-style `{name}` parameters.

*Available since v0.5.1*.

Authentication options:

* `:auth` - sets request authentication (via [`auth`](`Req.Steps.auth/1`) step.)
Expand Down
35 changes: 29 additions & 6 deletions lib/req/steps.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ defmodule Req.Steps do
:base_url,
:params,
:path_params,
:path_params_style,
:auth,
:form,
:json,
Expand Down Expand Up @@ -398,15 +399,34 @@ defmodule Req.Steps do
@doc """
Uses a templated request path.

By default, params in the URL path are expressed as strings prefixed with `:`. For example,
`:code` in `https://httpbin.org/status/:code`. If you want to use the `{code}` syntax,
set `path_params_style: :curly`. Param names must start with a letter and can contain letters,
digits, and underscores; this is true both for `:colon_params` as well as `{curly_params}`.

Path params are replaced in the request URL path. The path params are specified as a keyword
list of parameter names and values, as in the examples below. The values of the parameters are
converted to strings using the `String.Chars` protocol (`to_string/1`).

## Request Options

* `:path_params` - params to add to the templated path. Defaults to `[]`.
* `:path_params_style` (*available since v0.5.1*) - how path params are expressed.
wojtekmach marked this conversation as resolved.
Show resolved Hide resolved
Can be one of:

* `:colon` (default) for Plug-style parameters, such as
`https://httpbin.org/status/:code`.

* `:curly` for OpenAPI-style parameters, such as `https://httpbin.org/status/{code}`.

## Examples

iex> Req.get!("https://httpbin.org/status/:code", path_params: [code: 200]).status
200

iex> Req.get!("https://httpbin.org/status/{code}", path_params: [code: 201], path_params_style: :curly).status
200

"""
@doc step: :request
def put_path_params(request) do
Expand All @@ -424,18 +444,21 @@ defmodule Req.Steps do
end

defp apply_path_params(request, params) do
regex =
case Req.Request.get_option(request, :path_params_style, :colon) do
:colon -> ~r/:([a-zA-Z]{1}[\w_]*)/
:curly -> ~r/\{([a-zA-Z]{1}[\w_]*)\}/
end

update_in(request.url.path, fn
nil ->
nil

path ->
Regex.replace(~r/:([a-zA-Z]{1}[\w_]*)/, path, fn match, key ->
Regex.replace(regex, path, fn match, key ->
case params[String.to_existing_atom(key)] do
nil ->
match

value ->
value |> to_string() |> URI.encode()
nil -> match
value -> value |> to_string() |> URI.encode()
end
end)
end)
Expand Down
16 changes: 13 additions & 3 deletions test/req/steps_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,18 @@ defmodule Req.StepsTest do
end

test "put_path_params" do
req = Req.new(url: "http://foo/:id", path_params: [id: "abc|def"]) |> Req.Request.prepare()
assert URI.to_string(req.url) == "http://foo/abc%7Cdef"
req =
Req.new(url: "http://foo/:id{ola}", path_params: [id: "abc|def"]) |> Req.Request.prepare()

assert URI.to_string(req.url) == "http://foo/abc%7Cdef{ola}"

# With :curly style.

req =
Req.new(url: "http://foo/{id}:bar", path_params: [id: "abc|def"], path_params_style: :curly)
|> Req.Request.prepare()

assert URI.to_string(req.url) == "http://foo/abc%7Cdef:bar"
end

test "put_range" do
Expand Down Expand Up @@ -1138,7 +1148,7 @@ defmodule Req.StepsTest do
assert_receive :ping
refute_receive _

assert req.private == %{req_redirect_count: 3}
assert %{req_redirect_count: 3} = req.private
wojtekmach marked this conversation as resolved.
Show resolved Hide resolved
assert Exception.message(e) == "too many redirects (3)"
end

Expand Down
Loading