Packages
Bexio API Client for accessing the API described at https://docs.bexio.com.
Current section
Files
Jump to
Current section
Files
lib/bexio_api_client/helpers.ex
defmodule BexioApiClient.Helpers do
@moduledoc false
@doc """
Convert the given string into an Elixir date.
Examples:
iex> BexioApiClient.Helpers.to_date(nil)
nil
iex> BexioApiClient.Helpers.to_date("2013-02-22")
~D[2013-02-22]
"""
@spec to_date(String.t() | nil) :: Date.t() | nil
def to_date(nil), do: nil
def to_date(date) do
Date.from_iso8601!(date)
end
@doc """
Convert the given string into an Elixir decimal.
Examples:
iex> BexioApiClient.Helpers.to_decimal(nil)
nil
iex> BexioApiClient.Helpers.to_decimal("2.331")
#Decimal<2.331>
"""
@spec to_decimal(String.t() | nil) :: Decimal.t() | nil
def to_decimal(nil), do: nil
def to_decimal(decimal), do: Decimal.new(decimal)
# The format from Bexio is 2022-09-13 09:14:21
@doc """
Convert the given string into an Elixir date.
Examples:
iex> BexioApiClient.Helpers.to_datetime(nil)
nil
iex> BexioApiClient.Helpers.to_datetime("2022-09-13 09:14:21")
~N[2022-09-13 09:14:21]
"""
@spec to_datetime(String.t() | nil) :: DateTime.t() | nil
def to_datetime(nil), do: nil
def to_datetime(datetime) do
[zurich_date, zurich_time] = String.split(datetime, " ")
date = Date.from_iso8601!(zurich_date)
time = Time.from_iso8601!(zurich_time)
NaiveDateTime.new!(date, time)
end
def to_iso8601(nil), do: nil
def to_iso8601(%NaiveDateTime{} = n), do: NaiveDateTime.to_iso8601(n)
def to_iso8601(%DateTime{} = dt), do: DateTime.to_iso8601(dt)
def to_iso8601(%Date{} = dt), do: Date.to_iso8601(dt)
def to_naive_string(nil), do: nil
def to_naive_string(n), do: NaiveDateTime.to_string(n)
# The format from Bexio 3.0 is 2022-09-13T09:14:21+00:00
@doc """
Convert the given string into an elixir date using offsets
Examples:
iex> BexioApiClient.Helpers.to_offset_datetime(nil)
nil
iex> BexioApiClient.Helpers.to_offset_datetime("2022-09-13T09:14:21+02:00")
~U[2022-09-13 07:14:21Z]
"""
def to_offset_datetime(nil), do: nil
def to_offset_datetime(datetime) do
case DateTime.from_iso8601(datetime) do
{:ok, datetime, _offset} -> datetime
{:error, error} -> raise ArgumentError, message: error
end
end
@spec string_to_array(String.t() | nil) :: [integer()]
def string_to_array(nil), do: []
def string_to_array(string) do
string
|> String.split(",")
|> Enum.map(&String.to_integer/1)
|> Enum.uniq()
|> Enum.sort()
end
@type handler_callback_result_type :: any()
@type tesla_body_callback_type :: any()
@spec bexio_body_handling(
call :: (() -> tesla_body_callback_type),
callback :: (tesla_body_callback_type, Tesla.Env.t() -> handler_callback_result_type)
) ::
{:ok, handler_callback_result_type}
| {:error,
:invalid_access
| :unauthorized
| :not_found
| :length_required
| :invalid_data
| :could_not_be_saved
| :rate_limited
| :unexpected_api_condition
| :api_not_available}
def bexio_body_handling(call, callback) do
bexio_handling(call, fn body, env ->
{:ok, callback.(body, env)}
end)
end
@spec bexio_handling(
call :: (() -> tesla_body_callback_type),
callback :: (tesla_body_callback_type, Tesla.Env.t() -> handler_callback_result_type)
) ::
handler_callback_result_type
| {:error,
:invalid_access
| :unauthorized
| :not_found
| :length_required
| :invalid_data
| :could_not_be_saved
| :rate_limited
| :unexpected_api_condition
| :api_not_available}
def bexio_handling(call, callback) do
case call.() do
{:ok, %Tesla.Env{status: status, body: body} = env} when status in [200, 201, 301] ->
callback.(body, env)
{:ok, %Tesla.Env{status: status}} ->
{:error, error_code(status)}
{:error, error} ->
{:error, error}
end
end
defp error_code(400), do: :bad_request
defp error_code(401), do: :invalid_access
defp error_code(403), do: :unauthorized
defp error_code(404), do: :not_found
defp error_code(411), do: :length_required
defp error_code(415), do: :invalid_data
defp error_code(422), do: :could_not_be_saved
defp error_code(429), do: :rate_limited
defp error_code(500), do: :unexpected_api_condition
defp error_code(503), do: :api_not_available
@doc """
converts a list of maps ("id" => id, "name" => name) into a single map with all values converted to %{id: name}
Examples:
iex> BexioApiClient.Helpers.body_to_map([%{"id" => 1, "name" => "hello"}, %{"id" => 2, "name" => "world"}], nil)
%{1 => "hello", 2 => "world"}
"""
@spec body_to_map(list(%{String.t() => any()}), Tesla.Env.t()) :: %{integer() => String.t()}
def body_to_map(body, _env) do
body
|> Enum.map(fn %{"id" => id, "name" => name} -> {id, name} end)
|> Enum.into(%{})
end
@doc """
Converts the pattern %{"id" => id, "name" => name} into %{id: id, name: name}
Examples:
iex> BexioApiClient.Helpers.id_name(%{"id" => 1, "name" => "hello"}, nil)
%{id: 1, name: "hello"}
"""
@spec id_name(%{String.t() => any()}, Tesla.Env.t()) :: %{id: integer(), name: String.t()}
def id_name(%{"id" => id, "name" => name}, _env), do: %{id: id, name: name}
@doc """
Just interprets the delete result into either true or false depending on the success key in the map
Example:
iex> BexioApiClient.Helpers.success_response(%{"success" => true}, %Tesla.Env{})
true
iex> BexioApiClient.Helpers.success_response(%{"failure" => true}, %Tesla.Env{})
false
"""
@spec success_response(map(), Tesla.Env.t()) :: boolean()
def success_response(%{"success" => true}, _env), do: true
def success_response(_result, _env), do: false
end