Current section

Files

Jump to
plaid_elixir lib plaid utilities.ex
Raw

lib/plaid/utilities.ex

defmodule Plaid.Utilities do
@moduledoc """
Utility functions for Plaid.
"""
alias Plaid.{Account, Connect, Error, Institutions, LongTailInstitutions, Mfa,
Message, Transaction, TransactionType, Token}
alias Plaid.Account.Balance, as: AccountBalance
alias Plaid.Account.Meta, as: AccountMeta
alias Plaid.Institutions.Credentials, as: InstitutionsCredentials
alias Plaid.LongTailInstitutions.Fields, as: LongTailInstitutionsFields
alias Plaid.LongTailInstitutions.Products, as: LongTailInstitutionsProducts
alias Plaid.Mfa.Question, as: MfaQuestion
alias Plaid.Mfa.Mask, as: MfaMask
alias Plaid.Transaction.Meta, as: TransactionMeta
alias Plaid.Transaction.Meta.Location, as: TransactionMetaLocation
alias Plaid.Transaction.Meta.Location.Coordinates, as: TransactionMetaLocationCoordinates
alias Plaid.Transaction.Score, as: TransactionScore
alias Plaid.Transaction.Score.Location, as: TransactionScoreLocation
@doc """
Encodes parameters for HTTP request body.
Merges Plaid credentials with supplied parameters and encodes the result
for submission as the HTTP request body.
Returns string.
## Example
```
params = %{username: "plaid_test", password: "plaid_good", type: "wells",
options: %{webhook: "http://requestb.in/", login_only: true,
pending: true, list: true, start_date: "2015-01-01",
end_date: "2015-03-31"}}
cred = %{client_id: "test_id", secret: "test_secret"}
"username=plaid_test&password=plaid_good&..." = Plaid.Utilities.encode_params(params, cred)
```
"""
@spec encode_params(map, map) :: binary
def encode_params(params, cred \\ %{}) do
params
|> Map.merge(cred)
|> Map.to_list
|> Enum.map_join("&", fn x -> pair(x) end)
end
@doc """
Handles Plaid response.
Routes success responses to the decoder which explicitly maps them to
pre-defined structs based on the schema (provided as a parameter).
Unsuccessful responses are mapped to the Plaid.Error struct.
Returns a Plaid data struct or `HTTPoison.Error` struct.
## Example
```
{:ok, "test_bofa"} = Plaid.Utilities.handle_plaid_response(%HTTPoison.Response{body: "{...}"}, :token)
```
"""
@spec handle_plaid_response({atom, any}, atom) :: {atom, any}
def handle_plaid_response({:error, httpoison_error}, _schema), do: {:error, httpoison_error}
@spec handle_plaid_response(map, atom) :: {atom, binary | map}
def handle_plaid_response(response, schema) do
cond do
response.status_code in [200,201] ->
{:ok, decode_response(response.body, schema)}
true ->
{:error, Poison.decode!(response.body, as: %Error{})}
end
end
# Maps the HTTP response body to the corresponding schema based on the schema
# and HTTP response body format.
defp decode_response(body, schema) do
case schema do
:token ->
Poison.decode!(body, as: %Token{}) |> Map.get(:access_token)
:connect ->
case Poison.decode!(body) do
%{"access_token" => _, "accounts" => _, "transactions" => _} ->
map_transactions(body)
%{"access_token" => _, "accounts" => _} ->
map_accounts(body)
%{"access_token" => _, "mfa" => [%{"question" => _}|_], "type" => _} ->
map_mfa_question(body)
%{"access_token" => _, "mfa" => [%{"mask" => _, "type" => _}|_], "type" => _} ->
map_mfa_mask(body)
%{"access_token" => _, "mfa" => %{"message" => _}, "type" => _} ->
map_mfa_message(body)
%{"message" => _} ->
map_message(body)
end
:categories ->
case Poison.decode!(body) do
[_|_] ->
Poison.decode!(body, as: [%Plaid.Categories{}])
_ ->
Poison.decode!(body, as: %Plaid.Categories{})
end
:institutions ->
case Poison.decode!(body) do
[_|_] ->
map_institutions(body)
_ ->
map_institution(body)
end
:long_tail ->
case Poison.decode!(body) do
[] ->
[]
[_|_] ->
map_long_tail_institutions(body)
_ ->
map_long_tail_institution(body)
end
end
end
# Separates map key-value pairs into encoded strings to form the HTTP
# request body.
defp pair({key, value}) do
param_name = to_string(key) |> URI.encode_www_form
param_value =
cond do
is_map(value) ->
param_value = value |> Poison.encode!
true ->
param_value = to_string(value) |> URI.encode_www_form
end
"#{param_name}=#{param_value}"
end
defp map_transactions(body) do
Poison.decode!(body, as: %Connect{
accounts: [%Account{
balance: %AccountBalance{},
meta: %AccountMeta{}}],
transactions: [%Transaction{
meta: %TransactionMeta{
location: %TransactionMetaLocation{
coordinates: %TransactionMetaLocationCoordinates{}}},
type: %TransactionType{},
score: %TransactionScore{
location: %TransactionScoreLocation{}}}]
})
end
defp map_accounts(body) do
Poison.decode!(body, as: %Connect{
accounts: [%Account{
balance: %AccountBalance{},
meta: %AccountMeta{}}]
})
end
defp map_mfa_question(body) do
Poison.decode!(body, as: %Mfa{mfa: [%MfaQuestion{}]})
end
defp map_mfa_mask(body) do
Poison.decode!(body, as: %Mfa{mfa: [%MfaMask{}]})
end
defp map_mfa_message(body) do
Poison.decode!(body, as: %Mfa{mfa: %Message{}})
end
defp map_message(body) do
Poison.decode!(body, as: %Message{})
end
defp map_institutions(body) do
Poison.decode!(body, as: [%Institutions{credentials: %InstitutionsCredentials{}}])
end
defp map_institution(body) do
Poison.decode!(body, as: %Institutions{credentials: %InstitutionsCredentials{}})
end
defp map_long_tail_institutions(body) do
Poison.decode!(body, as: [%LongTailInstitutions{
products: %LongTailInstitutionsProducts{},
fields: [%LongTailInstitutionsFields{}]
}])
end
defp map_long_tail_institution(body) do
Poison.decode!(body, as: %LongTailInstitutions{
products: %LongTailInstitutionsProducts{},
fields: [%LongTailInstitutionsFields{}]
})
end
end