Page MenuHomePhorge

No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/tesla/middleware/json.ex b/lib/tesla/middleware/json.ex
index 576aa6c..b4d28eb 100644
--- a/lib/tesla/middleware/json.ex
+++ b/lib/tesla/middleware/json.ex
@@ -1,107 +1,110 @@
defmodule Tesla.Middleware.JSON do
# NOTE: text/javascript added to support Facebook Graph API.
# see https://github.com/teamon/tesla/pull/13
- @content_types ["application/json", "text/javascript"]
+ @default_content_types ["application/json", "text/javascript"]
@default_engine Poison
@doc """
Encode and decode response body as JSON
Available options:
- `:decode` - decoding function
- `:encode` - encoding function
- `:engine` - encode/decode engine, e.g `Poison` or `JSX` (defaults to Poison)
- `:engine_opts` - optional engine options
+ - `:decode_content_types` - list of additional decodable content-types
"""
def call(env, next, opts) do
opts = opts || []
env
|> encode(opts)
|> Tesla.run(next)
|> decode(opts)
end
def encode(env, opts) do
if encodable?(env) do
env
|> Map.update!(:body, &encode_body(&1, opts))
|> Tesla.Middleware.Headers.call([], %{"content-type" => "application/json"})
else
env
end
end
defp encode_body(%Stream{} = body, opts), do: encode_stream(body, opts)
defp encode_body(body, opts) when is_function(body), do: encode_stream(body, opts)
defp encode_body(body, opts), do: process(body, :encode, opts)
defp encode_stream(body, opts) do
Stream.map body, fn item -> encode_body(item, opts) <> "\n" end
end
def encodable?(env), do: env.body != nil
def decode(env, opts) do
- if decodable?(env) do
+ if decodable?(env, opts) do
Map.update!(env, :body, &process(&1, :decode, opts))
else
env
end
end
- def decodable?(env), do: decodable_body?(env) && decodable_content_type?(env)
+ def decodable?(env, opts), do: decodable_body?(env) && decodable_content_type?(env, opts)
def decodable_body?(env) do
(is_binary(env.body) && env.body != "") ||
(is_list(env.body) && env.body != [])
end
- def decodable_content_type?(env) do
+ def decodable_content_type?(env, opts) do
case env.headers["content-type"] do
nil -> false
- content_type -> Enum.any?(@content_types, &String.starts_with?(content_type, &1))
+ content_type -> Enum.any?(content_types(opts), &String.starts_with?(content_type, &1))
end
end
+ def content_types(opts), do: @default_content_types ++ Keyword.get(opts, :decode_content_types, [])
+
defp process(data, op, opts) do
with {:ok, value} <- do_process(data, op, opts) do
value
else
{:error, reason} -> raise %Tesla.Error{message: "JSON #{op} error: #{inspect reason}", reason: reason}
end
end
defp do_process(data, op, opts) do
if fun = opts[op] do # :encode/:decode
fun.(data)
else
engine = Keyword.get(opts, :engine, @default_engine)
opts = Keyword.get(opts, :engine_opts, [])
apply(engine, op, [data, opts])
end
end
end
defmodule Tesla.Middleware.DecodeJson do
def call(env, next, opts) do
opts = opts || []
env
|> Tesla.run(next)
|> Tesla.Middleware.JSON.decode(opts)
end
end
defmodule Tesla.Middleware.EncodeJson do
def call(env, next, opts) do
opts = opts || []
env
|> Tesla.Middleware.JSON.encode(opts)
|> Tesla.run(next)
end
end
diff --git a/test/tesla/middleware/json_test.exs b/test/tesla/middleware/json_test.exs
index f87aa3e..8e0257b 100644
--- a/test/tesla/middleware/json_test.exs
+++ b/test/tesla/middleware/json_test.exs
@@ -1,92 +1,117 @@
defmodule JsonTest do
use ExUnit.Case
use Tesla.Middleware.TestCase, middleware: Tesla.Middleware.JSON
use Tesla.Middleware.TestCase, middleware: Tesla.Middleware.DecodeJson
use Tesla.Middleware.TestCase, middleware: Tesla.Middleware.EncodeJson
defmodule Client do
use Tesla
plug Tesla.Middleware.JSON
adapter fn (env) ->
{status, headers, body} = case env.url do
"/decode" ->
{200, %{'Content-Type' => 'application/json'}, "{\"value\": 123}"}
"/encode" ->
{200, %{'Content-Type' => 'application/json'}, env.body |> String.replace("foo", "baz")}
"/empty" ->
{200, %{'Content-Type' => 'application/json'}, nil}
"/empty-string" ->
{200, %{'Content-Type' => 'application/json'}, ""}
"/invalid-content-type" ->
{200, %{'Content-Type' => 'text/plain'}, "hello"}
"/facebook" ->
{200, %{'Content-Type' => 'text/javascript'}, "{\"friends\": 1000000}"}
end
%{env | status: status, headers: headers, body: body}
end
end
- defmodule CustomClient do
- use Tesla
-
- plug Tesla.Middleware.DecodeJson, engine: Poison, engine_opts: [keys: :atoms]
-
- adapter fn (env) ->
- case env.url do
- "/decode" ->
- {200, %{'Content-Type' => 'application/json'}, "{\"value\": 123}"}
- end
- end
- end
-
-
test "decode JSON body" do
assert Client.get("/decode").body == %{"value" => 123}
end
test "do not decode empty body" do
assert Client.get("/empty").body == nil
end
test "do not decode empty string body" do
assert Client.get("/empty-string").body == ""
end
test "decode only if Content-Type is application/json or test/json" do
assert Client.get("/invalid-content-type").body == "hello"
end
test "encode body as JSON" do
assert Client.post("/encode", %{"foo" => "bar"}).body == %{"baz" => "bar"}
end
test "decode if Content-Type is text/javascript" do
assert Client.get("/facebook").body == %{"friends" => 1000000}
end
+ defmodule CustomClient do
+ use Tesla
+
+ plug Tesla.Middleware.DecodeJson, engine: Poison, engine_opts: [keys: :atoms]
+
+ adapter fn (env) ->
+ {status, headers, body} = case env.url do
+ "/decode" ->
+ {200, %{'Content-Type' => 'application/json'}, "{\"value\": 123}"}
+ end
+
+ %{env | status: status, headers: headers, body: body}
+ end
+ end
+
+ test "decode with custom engine options" do
+ assert CustomClient.get("/decode").body == %{value: 123}
+ end
+
+ defmodule CustomContentTypeClient do
+ use Tesla
+
+ plug Tesla.Middleware.JSON, decode_content_types: ["application/x-custom-json"]
+
+ adapter fn (env) ->
+ {status, headers, body} = case env.url do
+ "/decode" ->
+ {200, %{'Content-Type' => 'application/x-custom-json'}, "{\"value\": 123}"}
+ end
+
+ %{env | status: status, headers: headers, body: body}
+ end
+ end
+
+ test "decode if Content-Type specified in :decode_content_types" do
+ alias CustomContentTypeClient, as: CCTClient
+ assert CCTClient.get("/decode").body == %{"value" => 123}
+ end
+
defmodule EncodeDecodeJsonClient do
use Tesla
plug Tesla.Middleware.DecodeJson
plug Tesla.Middleware.EncodeJson
adapter fn (env) ->
{status, headers, body} = case env.url do
"/foo2baz" ->
{200, %{'Content-Type' => 'application/json'}, env.body |> String.replace("foo", "baz")}
end
%{env | status: status, headers: headers, body: body}
end
end
test "EncodeJson / DecodeJson work without options" do
alias EncodeDecodeJsonClient, as: EDJClient
assert EDJClient.post("/foo2baz", %{"foo" => "bar"}).body == %{"baz" => "bar"}
end
end

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jan 21, 2:33 AM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55640
Default Alt Text
(7 KB)

Event Timeline