Last active
January 18, 2017 03:42
-
-
Save geofflane/67aa78675a88effa2ca03fb096fc45ee to your computer and use it in GitHub Desktop.
HTTPoison Stateless API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@doc """ | |
Struct to hold the configurable portions of HTTPoision | |
""" | |
defmodule HTTPoison.Client do | |
defstruct process_url: &__MODULE__.process_url/1, | |
process_request_body: &__MODULE__.process_request_body/1, | |
process_request_headers: &__MODULE__.process_request_headers/1, | |
process_request_options: &__MODULE__.process_request_options/1, | |
process_response_body: &__MODULE__.process_response_body/1, | |
process_headers: &__MODULE__.process_headers/1, | |
process_status_code: &__MODULE__.process_status_code/1 | |
def process_url(url), do: url | |
def process_request_body(body), do: body | |
def process_request_headers(headers), do: headers | |
def process_request_options(opts), do: opts | |
def process_response_body(body), do: body | |
def process_headers(headers), do: headers | |
def process_status_code(status_code), do: status_code | |
end | |
@doc """ | |
API that takes the client and applies the configuration | |
""" | |
defmodule HTTPoison.API do | |
def post(client, url, body, headers \\ [], options \\ []) do | |
request(client, :post, url, body, headers, options) | |
end | |
def get(client, url, headers \\ [], options \\ []) do | |
request(client, :get, url, "", headers, options) | |
end | |
def delete(client, url, headers \\ [], options \\ []) do | |
request(client, :delete, url, "", headers, options) | |
end | |
# Make the actual request | |
# This uses an undocumented function in HTTPoision which is probably a bit sketchy | |
defp request(client, method, url, body, headers, options) do | |
url = | |
if Keyword.has_key?(options, :params) do | |
url <> "?" <> URI.encode_query(options[:params]) | |
else | |
url | |
end | |
url = client.process_url.(to_string(url)) | |
body = client.process_request_body.(body) | |
headers = client.process_request_headers.(headers) | |
options = client.process_request_options.(options) | |
HTTPoison.Base.request(__MODULE__, method, url, body, headers, options, client.process_status_code, client.process_headers, client.process_response_body) | |
end | |
end | |
# How you would use it in your own code | |
@doc """ | |
client = Example.Pulsar.client(:service) | |
HTTPosion.API.get(client, "/foo") | |
""" | |
defmodule Example.Pulsar do | |
require Logger | |
def client(key) do | |
%HTTPoison.Client{process_url: process_url(key), | |
process_request_body: &json_encode/1, | |
process_response_body: &json_decode/1, | |
process_request_headers: &ensure_json_header/1, | |
process_request_options: &increase_timeouts/1, | |
} | |
end | |
def process_url(key) do | |
endpoint = endpoint(key) | |
fn (url) -> | |
endpoint <> url | |
end | |
end | |
def json_encode(body) do | |
Logger.debug("Connection body: #{inspect body}") | |
body | |
|> Poison.encode!() | |
end | |
def json_decode(body) do | |
Logger.debug("Connection response: #{inspect body}") | |
case Poison.decode(body) do | |
{:ok, response} -> response | |
error -> error | |
end | |
end | |
def ensure_json_header(headers) do | |
Enum.into(headers, [{"Content-Type", "application/json"}]) | |
end | |
def increase_timeouts(opts) do | |
opts | |
|> PffLive.Pulsar.default_options(:timeout, 10000) | |
|> PffLive.Pulsar.default_options(:recv_timeout, 30000) | |
end | |
defp config(key) do | |
Application.fetch_env!(:example, key) | |
end | |
defp endpoint(key) do | |
config(key) | |
|> Keyword.get(:pulsar_endpoint) | |
end | |
def default_options(options, keyword, default) do | |
val = Keyword.get(options, keyword) | |
if val do | |
options | |
else | |
[{keyword, default} | options] | |
end | |
end | |
end | |
The behavior is interesting but doesn't seem to achieve what I want to do. Maybe a more generic idea:
How could someone create a generic JSON client that set the proper headers, transformed the body on the request and response, etc. but only change the endpoint so that they had an instance that they could use to call ServiceA and a different instance they could use to call ServiceB? That's the fundamental problem I was trying to solve. The only way I could see to do that was to turn the "client" into data instead of having it hardcoded in a module.
One other way is to pass an option which is always the last argument. Then build the url with the option passed?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There are multiple ways to achieve this with
HTTPoison
. You could redefine request/5 and receive the endpoint that you intend as an option. Then the "base url" will be defined at that stage.I also experimented a bit with a
Behaviour
: https://github.com/edgurgel/httpehaviour