Packages

Middleware for Tesla HTTP client library that encodes and decodes XML-RPC

Current section

Files

Jump to
tesla_middleware_xmlrpc lib tesla middleware xmlrpc.ex
Raw

lib/tesla/middleware/xmlrpc.ex

defmodule Tesla.Middleware.XMLRPC do
@moduledoc """
Encode requests and decode responses as [XML-RPC](http://wikipedia.org/wiki/XML-RPC).
## Examples
```
defmodule FooClient do
@api_url Application.compile_env(:foo_client, :api_url, "http://localhost:8080/")
use Tesla
plug Tesla.Middleware.BaseUrl, @api_url
plug Tesla.Middleware.XMLRPC
def call(method_name, params, opts \\ []) do
body = %XMLRPC.MethodCall{method_name: method_name, params: params}
case post("/RPC2", body, opts) do
{:ok, %{status: 200, body: %XMLRPC.MethodResponse{param: result}}} ->
{:ok, result}
{:ok, %{status: 200, body: %XMLRPC.Fault{fault_code: code, fault_string: reason}}} ->
{:error, {:fault, code, reason}}
{:ok, response} ->
{:error, {:unexpected_response, response}}
{:error, _reason} = error ->
error
end
end
end
```
## Options
- `:xmlrpc_opts` - options passed to the XMLRPC encoder and decoder
"""
@behaviour Tesla.Middleware
@default_encode_content_type "text/xml"
@default_content_types ["text/xml"]
@impl Tesla.Middleware
def call(env, next, opts) do
opts = opts || []
with {:ok, env} <- encode(env, opts),
{:ok, env} <- Tesla.run(env, next) do
decode(env, opts)
end
end
# Encode request body as XML-RPC.
@doc false
def encode(env, opts) do
with true <- encodable?(env),
{:ok, body} <- encode_body(env.body, opts) do
{:ok,
env
|> Tesla.put_body(body)
|> Tesla.put_headers([{"content-type", encode_content_type(opts)}])}
else
false -> {:ok, env}
error -> error
end
end
@spec encode_body(XMLRPC.MethodCall.t(), keyword()) ::
{:ok, iodata()} | {:ok, binary()} | {:error, {any, binary()}}
defp encode_body(body, tesla_opts) do
opts = tesla_opts[:xmlrpc_opts] || []
XMLRPC.encode(body, opts)
end
defp encode_content_type(opts) do
Keyword.get(opts, :encode_content_type, @default_encode_content_type)
end
defp encodable?(%{body: %XMLRPC.MethodCall{}}), do: true
defp encodable?(_), do: false
# Decode response body as XML-RPC.
@doc false
def decode(env, opts) do
with true <- decodable?(env, opts),
{:ok, body} <- decode_body(env.body, opts) do
{:ok, %{env | body: body}}
else
false -> {:ok, env}
error -> error
end
end
@spec decode_body(iodata(), keyword()) ::
{:ok, XMLRPC.Fault.t() | XMLRPC.MethodResponse.t()} | {:error, binary()}
defp decode_body(body, tesla_opts) do
opts = tesla_opts[:xmlrpc_opts] || []
XMLRPC.decode(body, opts)
end
defp decodable?(env, opts), do: decodable_body?(env) && decodable_content_type?(env, opts)
defp decodable_body?(env) do
(is_binary(env.body) && env.body != "") || (is_list(env.body) && env.body != [])
end
defp decodable_content_type?(env, opts) do
case Tesla.get_header(env, "content-type") do
nil -> true
content_type -> Enum.any?(content_types(opts), &String.starts_with?(content_type, &1))
end
end
defp content_types(opts) do
@default_content_types ++ Keyword.get(opts, :decode_content_types, [])
end
end