defmodule TeslaCase.Middleware do @moduledoc """ Tesla middleware for converting the body keys of the request and response. This middleware will convert all keys from the body of the request using the appropriate function before performing it and will convert all keys from the body of the response to snake case. ## Examples ``` defmodule MyClient do use Tesla plug TeslaCase.Middleware # use defaults # or plug TeslaCase.Middleware, encode: &Recase.to_camel/1, serializer: &Recase.Enumerable.stringify_keys/2 end ``` ## Options - `:serializer` - serializer function with arity 2, receives the data as the first parameter and the `:encode` as the second parameter, (defaults to `&Recase.Enumerable.stringify_keys/2`) - `:encode` - encoding function, e.g `&Recase.to_camel/1`, `&Recase.to_pascal/1` (defaults to `&Recase.to_camel/1`) """ @behaviour Tesla.Middleware @impl true def call(env, next, opts) do serializer = Keyword.get(opts, :serializer, &Recase.Enumerable.stringify_keys/2) encode = Keyword.get(opts, :encode, &Recase.to_camel/1) decode = &Recase.to_snake/1 env |> request(serializer, encode) |> Tesla.run(next) |> response(serializer, decode) end defp request(%{body: nil} = env, _serializer, _encode) do env end defp request(%{body: body} = env, serializer, encode) do %{env | body: converter(body, serializer, encode)} end defp response({:ok, %{body: nil}} = env, _serializer, _decode) do env end defp response({:ok, env}, serializer, decode) do env = Map.update!(env, :body, &converter(&1, serializer, decode)) {:ok, env} end defp response({:error, error}, _serializer, _decode) do {:error, error} end defp converter(data, serializer, converter) do apply(serializer, [data, converter]) end end