Maxwell

Build StatusInline docsCoveralls CoverageHex.pm

Maxwell is an HTTP client that provides a common interface over :httpc, :ibrowse, :hackney.

Documentation for Maxwell is available online.

Usage

Use Maxwell.Builder module to create the API wrappers.

defmodule GitHubClient do
#generate 4 function get/1, get!/1 patch/1 patch!/1 function
use Maxwell.Builder, ~w(get patch)a
middleware Maxwell.Middleware.BaseUrl, "https://api.github.com"
middleware Maxwell.Middleware.Headers, %{'Content-Type': "application/vnd.github.v3+json", 'User-Agent': 'zhongwenool'}
middleware Maxwell.Middleware.Opts, [connect_timeout: 3000]
middleware Maxwell.Middleware.Json
middleware Maxwell.Middleware.Logger
adapter Maxwell.Adapter.Hackney # default adapter is Maxwell.Adapter.Httpc
#List public repositories for the specified user.
#:hackney.request(:get,
# 'https://api.github.com/users/zhongwencool/repos',
# ['Content-Type': "application/vnd.github.v3+json", 'User-Agent': 'zhongwenool'],
# [],
# [connect_timeout: 3000])
def user_repos(username) do
put_path("/users/" <> username <> "/repos") |> get
end
# Edit owner repositories
# :hackney.request(:patch,
# 'https://api.github.com/repos/owner/repo',
# ['Content-Type': "application/vnd.github.v3+json", 'User-Agent': 'zhongwenool'],
# "{\"name\":\"name\",\"description\":\"desc\"}",
# [connect_timeout: 3000])
def edit_repo_desc(owner, repo, name, desc) do
new
|> put_path("/repos/#{owner}/#{repo}")
|> put_req_body(%{name: name, description: desc})
|> patch
end
end
MIX_ENV=TEST iex -S mix
iex(1)> GitHubClient.
edit_repo_desc/4 get!/0 get!/1
get!/2 get/0 get/1
patch!/0 patch!/1 patch!/2
patch/0 patch/1 user_repos/1
iex(1)> GitHubClient.user_repos("zhongwencool")
22:23:42.307 [info] get https://api.github.com <<<200(3085.772ms)
%Maxwell.Conn{method: :get, opts: [connect_timeout: 3000, recv_timeout: 20000]
...(truncated)

if you don't want to defined a client module:

iex(2)> Maxwell.Conn.new("http://httpbin.org/drip") |> Maxwell.Conn.put_query_string(%{numbytes: 25, duration: 1, delay: 1, code: 200}) |> Maxwell.get
{:ok,
%Maxwell.Conn{method: :get, opts: [], path: "",
query_string: %{code: 200, delay: 1, duration: 1, numbytes: 25},
req_body: nil, req_headers: %{}, resp_body: '*************************',
resp_headers: %{"access-control-allow-credentials" => {"access-control-allow-credentials",
"true"},
"access-control-allow-origin" => {"access-control-allow-origin", "*"},
"connection" => {"connection", "keep-alive"},
"content-length" => {"content-length", "25"},
"content-type" => {"content-type", "application/octet-stream"},
"date" => {"date", "Sun, 18 Dec 2016 14:32:38 GMT"},
"server" => {"server", "nginx"}}, state: :sent, status: 200,
url: "http://httpbin.org/drip"}}

Maxwell.Conn helper functions

new(request_url_string)
|> put_query_string(request_query_map)
|> put_req_header(request_headers_map)
|> put_option(request_opts_keyword_list)
|> put_req_body(request_body_term)
|> YourClient.{http_method}!
|> get_resp_body

For more examples see h Maxwell.Conn.XXX

Response result

{:ok,
%Maxwell{
resp_headers: reponse_headers_map,
status: reponse_http_status_integer,
resp_body: reponse_body_term,
url: request_urlwithquery_string,
}}
# or
{:error, reason_term, conn}

Request Examples

defmodule Client do
#generate 4 function get/1, get!/1 post/1 post!/1 function
use Maxwell.Builder, ~w(get post)a
middleware Maxwell.Middleware.BaseUrl, "http://httpbin.org"
middleware Maxwell.Middleware.Headers, %{"Content-Type" => "application/json"}
middleware Maxwell.Middleware.Opts, [connect_timeout: 5000, recv_timeout: 10000]
middleware Maxwell.Middleware.Json
middleware Maxwell.Middleware.Logger
adapter Maxwell.Adapter.Hackney
@doc """
Simple get request
Get origin ip
"""
def get_ip() do
new
|> put_path("/ip")
|> get!
|> get_resp_body("origin")
end
@doc """
Post whole file once
###Example
Client.post_file_once("./mix.exs")
"""
def post_file_once(filepath) do
new
|> put_path("/post")
|> put_req_body({:file, filepath})
|> post!
|> get_resp_body("data")
end
@doc """
Post whole file by chunked
###Example
Client.post_file_chunked("./mix.exs")
"""
def post_file_chunked(filepath) do
new
|> put_path("/post")
|> put_req_header("transfer_encoding", "chunked")
|> put_req_body({:file, filepath})
|> post!
|> get_resp_body("data")
end
@doc """
Post by stream
###Example
["1", "2", "3"] |> Stream.map(fn(x) -> List.duplicate(x, 2) end) |> Client.post_stream
"""
def post_stream(stream) do
new
|> put_path("/post")
|> put_req_body(stream)
|> post!
|> get_resp_body("data")
end
@doc """
Post multipart form
###Example
Client.post_multipart_form({:multipart, [{:file, "./mix.exs"}]})
"""
def post_multipart_form(multipart) do
new
|> put_path("/post")
|> put_req_body(multipart)
|> post!
|> get_resp_body("data")
end
end

Installation

  1. Add maxwell to your list of dependencies in mix.exs:
def deps do
[{:maxwell, "~> 2.1.0"}]
end
  1. Ensure maxwell has started before your application:
def application do
[applications: [:maxwell]] # **also add your adapter(ibrowse,hackney) here **
end

Adapters

Maxwell has support for different adapters that do the actual HTTP request processing.

httpc

Maxwell has built-in support for httpc Erlang HTTP client.

To use it simply include adapter Maxwell.Adapter.Httpc line in your API client definition. Setting global default adapter

config :maxwell,
default_adapter: Maxwell.Adapter.Httpc

ibrowse

Maxwell has built-in support for ibrowse Erlang HTTP client.

To use it simply include adapter Maxwell.Adapter.Ibrowse line in your API client definition. Setting global default adapter

config :maxwell,
default_adapter: Maxwell.Adapter.Ibrowse

NOTE: Remember to include :ibrowse in applications list.

hackney

Maxwell has built-in support for hackney Erlang HTTP client.

To use it simply include adapter Maxwell.Adapter.Hackney line in your API client definition. Setting global default adapter

config :maxwell,
default_adapter: Maxwell.Adapter.Hackney

NOTE: Remember to include :hackney in applications list.

Available Middleware

@middleware Maxwell.Middleware.EncodeJson, [encode_content_type: "text/javascript", encode_func: &other_json_lib.encode/1]
@middleware Maxwell.Middleware.DecodeJson, [decode_content_types: ["yourowntype"], decode_func: &other_json_lib.decode/1]

See more by h Maxwell.Middleware.{name}

Writing your own middleware

See Maxwell.Middleware.BaseUrl and Maxwell.Middleware.DecodeJson

TODO

Test

mix test

License

See the LICENSE file for license rights and limitations (MIT).