diff --git a/lib/req.ex b/lib/req.ex index 913487f..819cfde 100644 --- a/lib/req.ex +++ b/lib/req.ex @@ -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_style`](`Req.Steps.put_path_params_style/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.) diff --git a/lib/req/steps.ex b/lib/req/steps.ex index 7998a7a..48a95f6 100644 --- a/lib/req/steps.ex +++ b/lib/req/steps.ex @@ -26,6 +26,7 @@ defmodule Req.Steps do :base_url, :params, :path_params, + :path_params_style, :auth, :form, :json, @@ -71,6 +72,7 @@ defmodule Req.Steps do put_base_url: &Req.Steps.put_base_url/1, auth: &Req.Steps.auth/1, put_params: &Req.Steps.put_params/1, + put_path_params_style: &Req.Steps.put_path_params_style/1, put_path_params: &Req.Steps.put_path_params/1, put_range: &Req.Steps.put_range/1, cache: &Req.Steps.cache/1, @@ -398,6 +400,14 @@ 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` (see `put_path_params_style/1`). + + 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 `[]`. @@ -424,23 +434,39 @@ defmodule Req.Steps do end defp apply_path_params(request, params) do + regex = + case Req.Request.get_private(request, :path_params_style) || raise("missing") 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) end + @doc """ + TODO + """ + @doc since: "0.5.1" + @doc step: :request + def put_path_params_style(request) do + Req.Request.put_private( + request, + :path_params_style, + Req.Request.get_option(request, :path_params_style, :colon) + ) + end + @doc """ Adds params to request query string. diff --git a/test/req/steps_test.exs b/test/req/steps_test.exs index e8b8aa1..8df755a 100644 --- a/test/req/steps_test.exs +++ b/test/req/steps_test.exs @@ -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}" + end + + test "put_path_params_style" do + 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 @@ -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 assert Exception.message(e) == "too many redirects (3)" end