Skip to content

Commit

Permalink
Adding decoder at definition stage
Browse files Browse the repository at this point in the history
  • Loading branch information
nsomar committed Mar 8, 2016
1 parent 8c245dc commit a525eb7
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 13 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,23 @@ Accept-Language: en-US
Notice the use of quotes in the `"Accept-Language"`. This is needed since `Accept-Language` is not a valid atom name. In order to solve that, add quotation around atoms.

### Decoding HTTP Response
`EXRequester` allows you to pass an anonymous function to be used as response parser. For example, we can pass a decoder when calling `get_resource`.
`EXRequester` allows you to pass an anonymous function to be used as response parser. This anonymouse function can be definied in two places.

First, You can pass the anonymouse function at the function definition, For example:

```elixir
defmodule SampleAPI do
....
defreq get_resource(fn response ->
"Value is " <> response.body
end)
end
```

When calling `get_resource` the HTTP response of type `EXRequester.Response` will be sent to the passed anonymous function.
Using this way, you can create a response decoder in place.

Alternatively, you can pass a response decoder when calling the method pass a decoder as a parameter when calling `get_resource` For example:

```elixir
SampleAPI.client("http://base_url.com")
Expand Down
16 changes: 15 additions & 1 deletion lib/request.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule EXRequester.Request do
Structure that holds a request
"""

defstruct [:method, :base_url, :path, :headers_template, :body, :query_keys]
defstruct [:method, :base_url, :path, :headers_template, :body, :query_keys, :decoder]

@doc """
Return a new request
Expand Down Expand Up @@ -48,6 +48,20 @@ defmodule EXRequester.Request do
Map.put(request, :body, body)
end

@doc """
Adds a decoder block that will be called with the response
Parameters:
* `request` - request to update
* `decoder` - decoder block to call
"""
def add_decoder(request, nil), do: request

def add_decoder(request, decoder) do
Map.put(request, :decoder, decoder)
end

@doc """
Adds query keys to the request
Expand Down
42 changes: 33 additions & 9 deletions lib/requester.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ defmodule EXRequester do
Key1: :key1
]
@get "/path/to/resource/{resource_id}"
defreq get_resource(resource_id: resource_id, auth: auth, key1: key1)
defreq get_resource
end
Then to call it:
Expand All @@ -45,29 +45,50 @@ defmodule EXRequester do
`http://base_url.com/path/to/resource/123`
The `Authorization` and `Key1` headers will also be set.
If you want to decode the response, pass a decoder as a parameter when calling `get_resource`
For example:
If you want to decode the response, you can do it in two places:
At the function definition, For example:
defmodule SampleAPI do
....
defreq get_resource(fn response ->
"Value is " <> response.body
end)
end
When calling `get_resource` the HTTP response of type `EXRequester.Response` will be sent to the passed anonymous function.
Using this way, you can create a response decoder in place.
Alternatively, you can pass a response decoder when calling the method pass a decoder as a parameter when calling `get_resource` For example:
SampleAPI.client("http://base_url.com")
|> SampleAPI.get_resource(resource_id: 123, auth1: "1", key1: "2", decoder: fn response ->
"Value is " <> response.body
end)
The anonymous function passed to decoder will receive an `EXRequester.Response`. This function can parse the response and return a parsed response. The parsed response will be finally returned.
Note: The decoder passed when calling the method overwrites the decoder declated when defining the method in the module.
The example above returns `"Value is Content of body"`
"""
defmacro defreq(head) do
post_defreq(head)
end

def post_defreq({function_name, _, [decoder]}) do
define_functions(function_name, decoder)
end

def post_defreq({function_name, _, _}) do
define_functions(function_name)
define_functions(function_name, nil)
end

defp define_functions(function_name) do
defp define_functions(function_name, decoder) do
quote bind_quoted: [
function_name: function_name] do
function_name: function_name,
decoder: Macro.escape(decoder)] do

[{request_method, request_path}] = get_request_path_and_method
headers = get_request_headers
Expand All @@ -77,6 +98,7 @@ defmodule EXRequester do
EXRequester.Request.new(method: unquote(request_method), path: unquote(request_path))
|> EXRequester.Request.add_headers_keys(unquote(headers))
|> EXRequester.Request.add_query_keys(unquote(query))
|> EXRequester.Request.add_decoder(unquote(decoder))
end

proposed_method =
Expand Down Expand Up @@ -137,6 +159,7 @@ defmodule EXRequester do
request = unquote(request)
|> EXRequester.Request.add_body(params[:body])
|> EXRequester.Request.add_base_url(client.url)
|> EXRequester.Request.add_decoder(params[:decoder])

perform_request_and_parse(unquote(function_name), params, request)
end
Expand All @@ -158,17 +181,18 @@ defmodule EXRequester do

def perform_request_and_parse(function_name, params, request) do
check_called_correctly(function_name, params, request)

request_performer.do_request(request, params)
|> parse_response(params[:decoder])
|> parse_response(request)
end

defp request_performer do
Application.get_env(:exrequester, :request_performer, EXRequester.Performer.HTTPotion)
end

defp parse_response(response, nil), do: response
defp parse_response(response, %{decoder: nil}), do: response

defp parse_response(response, decoder) do
defp parse_response(response, %{decoder: decoder}) do
decoder.(response)
end

Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule EXRequester.Mixfile do
source_url: "https://github.com/oarrabi/exrequester",
docs: [ extras: ["README.md"] ],
description: description,
version: "0.1.0",
version: "0.3.0",
elixir: "~> 1.0",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
Expand Down
1 change: 0 additions & 1 deletion test/request_param_checker_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ defmodule EXRequest.ParamsCheckerTests do
assert res == {:error, [missing: [], extra: ["name"]]}
end


test "it checks that the params passed match the url and header" do
url = "users/{name}"
res = EXRequest.ParamsChecker.check_params(url, [:auth], [:name, :auth])
Expand Down
36 changes: 36 additions & 0 deletions test/requester_decoder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,40 @@ defmodule EXRequester.DEcoderTests do
assert res.body == "123"
end

test "it return passes the response to the body" do
defmodule TestAPIA8 do
use EXRequester

@put "user/{id}"
defreq get_status fn response ->
"before-" <> response.body <> "-after"
end

end

res = TestAPIA8.client("https://httpbin.org")
|> TestAPIA8.get_status(id: 1)

assert res == "before-123-after"
end

test "it overwrites the module level decoder" do
defmodule TestAPIA9 do
use EXRequester

@put "user/{id}"
defreq get_status fn response ->
"before-" <> response.body <> "-after"
end

end

res = TestAPIA9.client("https://httpbin.org")
|> TestAPIA9.get_status(id: 1, decoder: fn response ->
"before2222-" <> response.body <> "-after"
end)

assert res == "before2222-123-after"
end

end

0 comments on commit a525eb7

Please sign in to comment.