Skip to content

Commit

Permalink
Add Finch adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
adriankumpf authored and teamon committed Sep 10, 2020
1 parent f4444c2 commit 5b411ee
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ Tesla supports multiple HTTP adapter that do the actual HTTP request processing.
- [`Tesla.Adapter.Ibrowse`](https://hexdocs.pm/tesla/Tesla.Adapter.Ibrowse.html) - [ibrowse](https://github.com/cmullaparthi/ibrowse), "Erlang HTTP client"
- [`Tesla.Adapter.Gun`](https://hexdocs.pm/tesla/Tesla.Adapter.Gun.html) - [gun](https://github.com/ninenines/gun), "HTTP/1.1, HTTP/2 and Websocket client for Erlang/OTP"
- [`Tesla.Adapter.Mint`](https://hexdocs.pm/tesla/Tesla.Adapter.Mint.html) - [mint](https://github.com/elixir-mint/mint), "Functional HTTP client for Elixir with support for HTTP/1 and HTTP/2"
- [`Tesla.Adapter.Finch`](https://hexdocs.pm/tesla/Tesla.Adapter.Finch.html) - [finch](https://github.com/keathley/finch), "An HTTP client with a focus on performance, built on top of [Mint](https://github.com/elixir-mint/mint) and [NimblePool](https://github.com/dashbitco/nimble_pool)."

When using adapter other than httpc remember to add it to the dependencies list in `mix.exs`

Expand Down
88 changes: 88 additions & 0 deletions lib/tesla/adapter/finch.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
if Code.ensure_loaded?(Finch) do
defmodule Tesla.Adapter.Finch do
@moduledoc """
Adapter for [finch](https://github.com/keathley/finch).
Remember to add `{:finch, "~> 0.3"}` to dependencies. Also, you need to
recompile tesla after adding the `:finch` dependency:
```
mix deps.clean tesla
mix compile
```
## Example usage
In order to use Finch, you must start it and provide a `:name`. For example,
in your supervision tree:
```elixir
children = [
{Finch, name: MyFinch}
]
```
You must provide the same name to this adapter:
```
# set globally in config/config.exs
config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}
# set per module
defmodule MyClient do
use Tesla
adapter Tesla.Adapter.Finch, name: MyFinch
end
```
## Adapter specific options
* `:name` - The `:name` provided to Finch (**required**).
## [Finch options](https://hexdocs.pm/finch/Finch.html#request/3)
* `:pool_timeout` - This timeout is applied when a connection is checekd
out from the pool. Default value is `5_000`.
* `:receive_timeout` - The maximum time to wait for a response before
returning an error. Default value is `15_000`.
"""
@behaviour Tesla.Adapter
alias Tesla.Multipart

@impl Tesla.Adapter
def call(%Tesla.Env{} = env, opts) do
opts = Tesla.Adapter.opts(env, opts)

name = Keyword.fetch!(opts, :name)
url = Tesla.build_url(env.url, env.query)
req_opts = Keyword.take(opts, [:pool_timeout, :receive_timeout])

case request(name, env.method, url, env.headers, env.body, req_opts) do
{:ok, %Finch.Response{status: status, headers: headers, body: body}} ->
{:ok, %Tesla.Env{env | status: status, headers: headers, body: body}}

{:error, mint_error} ->
{:error, Exception.message(mint_error)}
end
end

defp request(name, method, url, headers, %Multipart{} = mp, opts) do
headers = headers ++ Multipart.headers(mp)
body = Multipart.body(mp) |> Enum.to_list()

request(name, method, url, headers, body, opts)
end

defp request(_name, _method, _url, _headers, %Stream{}, _opts) do
raise "Streaming is not supported by this adapter!"
end

defp request(name, method, url, headers, body, opts) do
Finch.build(method, url, headers, body)
|> Finch.request(name, opts)
end
end
end
12 changes: 7 additions & 5 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ defmodule Tesla.Mixfile do
#
# Type `mix help compile.app` for more information
def application do
[applications: applications(Mix.env())]
[applications: apps(Mix.env())]
end

def applications(:test), do: applications(:dev) ++ [:httparrot, :hackney, :ibrowse, :gun]
def applications(_), do: [:logger, :ssl, :inets]
def apps(:test), do: apps(:dev) ++ [:httparrot, :hackney, :ibrowse, :gun, :finch]
def apps(_), do: [:logger, :ssl, :inets]

defp description do
"HTTP client library, with support for middleware and multiple adapters."
Expand Down Expand Up @@ -60,7 +60,8 @@ defmodule Tesla.Mixfile do
{:ibrowse, "~> 4.4.0", optional: true},
{:hackney, "~> 1.6", optional: true},
{:gun, "~> 1.3", optional: true},
{:castore, "~> 0.1", optional: true},
{:finch, "~> 0.3", optional: true},
{:castore, "~> 0.1", optional: true, override: true},
{:mint, "~> 1.0", optional: true},

# json parsers
Expand Down Expand Up @@ -97,7 +98,8 @@ defmodule Tesla.Mixfile do
Tesla.Adapter.Hackney,
Tesla.Adapter.Httpc,
Tesla.Adapter.Ibrowse,
Tesla.Adapter.Mint
Tesla.Adapter.Mint,
Tesla.Adapter.Finch
],
Middlewares: [
Tesla.Middleware.BaseUrl,
Expand Down
7 changes: 4 additions & 3 deletions test/support/adapter_case.ex
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
defmodule Tesla.AdapterCase do
defmacro __using__(adapter: adapter) do
defmacro __using__(opts) do
quote do
@adapter unquote(adapter)
@adapter unquote(Keyword.fetch!(opts, :adapter))
@adapter_opts unquote(opts[:adapter_opts] || [])
@http "http://localhost:#{Application.get_env(:httparrot, :http_port)}"
@https "https://localhost:#{Application.get_env(:httparrot, :https_port)}"

defp call(env, opts \\ []) do
@adapter.call(env, opts)
@adapter.call(env, Keyword.merge(opts, @adapter_opts))
end
end
end
Expand Down
16 changes: 16 additions & 0 deletions test/tesla/adapter/finch_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule Tesla.Adapter.FinchTest do
use ExUnit.Case

@finch_name MyFinch

use Tesla.AdapterCase, adapter: Tesla.Adapter.Finch, adapter_opts: [name: @finch_name]
use Tesla.AdapterCase.Basic
use Tesla.AdapterCase.Multipart
# use Tesla.AdapterCase.StreamRequestBody
use Tesla.AdapterCase.SSL

setup do
start_supervised!({Finch, name: @finch_name})
:ok
end
end

0 comments on commit 5b411ee

Please sign in to comment.