defmodule Braintree.Search do
@moduledoc """
This module performs advanced search on a resource.
For additional reference see:
https://developers.braintreepayments.com/reference/general/searching/search-fields/ruby
"""
alias Braintree.ErrorResponse, as: Error
alias Braintree.HTTP
@doc """
Perform an advanced search on a given resource and create new structs
based on the initializer given.
## Example
search_params = %{first_name: %{is: "Jenna"}}
{:ok, customers} = Braintree.Search.perform(search_params, "customers", &Braintree.Customer.new/1)
"""
@spec perform(map, String.t(), fun(), Keyword.t()) :: {:ok, [any]} | {:error, Error.t()}
def perform(params, resource, initializer, opts \\ []) when is_map(params) do
with {:ok, payload} <- HTTP.post(resource <> "/advanced_search_ids", %{search: params}, opts) do
fetch_all_records(payload, resource, initializer, opts)
end
end
defp fetch_all_records(%{"search_results" => %{"ids" => []}}, _resource, _initializer, _opts) do
{:error, :not_found}
end
defp fetch_all_records(
%{"search_results" => %{"page_size" => page_size, "ids" => ids}},
resource,
initializer,
opts
) do
records =
ids
|> Enum.chunk_every(page_size)
|> Enum.flat_map(fn ids_chunk ->
fetch_records_chunk(ids_chunk, resource, initializer, opts)
end)
{:ok, records}
end
# Credit card verifications and transactions are an odd case because path to endpoints is
# different from the object name in the XML.
defp fetch_records_chunk(ids, resource, initializer, opts)
when is_list(ids) and resource in ~w(verifications transactions) do
search_params = %{search: %{ids: ids}}
key_name = "credit_card_#{resource}"
with {:ok, %{^key_name => data}} <-
HTTP.post(resource <> "/advanced_search", search_params, opts) do
initializer.(data)
end
end
defp fetch_records_chunk(ids, resource, initializer, opts) when is_list(ids) do
search_params = %{search: %{ids: ids}}
with {:ok, %{^resource => data}} <-
HTTP.post(resource <> "/advanced_search", search_params, opts) do
initializer.(data)
end
end
end