Current section

Files

Jump to
cloudflare_api lib utils.ex
Raw

lib/utils.ex

defmodule CloudflareApi.Utils do
@doc ~S"""
Macro that makes a function public in test, private in non-test
See: https://stackoverflow.com/a/47598190/2062384
"""
defmacro defp_testable(head, body \\ nil) do
if Mix.env() == :test do
quote do
def unquote(head) do
unquote(body[:do])
end
end
else
quote do
defp unquote(head) do
unquote(body[:do])
end
end
end
end
@doc ~S"""
Easy drop-in to a pipe to inspect the return value of the previous function.
## Examples
conn
|> put_status(:not_found)
|> put_view(CloudflareApiWeb.ErrorView)
|> render(:"404")
|> pry_pipe()
"""
def pry_pipe(retval, arg1 \\ nil, arg2 \\ nil, arg3 \\ nil, arg4 \\ nil) do
require IEx
IEx.pry()
retval
end
@doc ~S"""
Convert a map with `String` keys into a map with `Atom` keys.
## Examples
iex> CloudflareApi.Utils.map_string_keys_to_atoms(%{"one" => "one", "two" => "two"})
%{one: "one", two: "two"}m
"""
def map_string_keys_to_atoms(map) do
for {key, val} <- map, into: %{} do
{String.to_atom(key), val}
end
end
@doc ~S"""
Convert a map with `String` keys into a map with `Atom` keys.
## Examples
iex> CloudflareApi.Utils.map_atom_keys_to_strings(%{one: "one", two: "two"})
%{"one" => "one", "two" => "two"}
"""
def map_atom_keys_to_strings(map) do
for {key, val} <- map, into: %{} do
{Atom.to_string(key), val}
end
end
@doc ~S"""
Converts a struct to a regular map by deleting the `:__meta__` key
## Examples
CloudflareApi.Utils.struct_to_map(%Something{hello: "world"})
%{hello: "world"}
"""
def struct_to_map(struct) do
Map.from_struct(struct)
|> Map.delete(:__meta__)
end
@doc ~S"""
Generate a new UUIDv4
## Examples
CloudflareApi.Utils.uuidgen()
"4c2fd8d3-a6e3-4e4b-a2ce-3f21456eeb85"
"""
def uuidgen(),
do: Ecto.UUID.generate()
@doc ~S"""
Quick regex check to see if the supplied `string` is a valid UUID
Check is done by simple regular expression and is not overly sophisticated.
Return true || false
## Examples
iex> CloudflareApi.Utils.is_uuid?(nil)
false
iex> CloudflareApi.Utils.is_uuid?("hello world")
false
iex> CloudflareApi.Utils.is_uuid?("4c2fd8d3-a6e3-4e4b-a2ce-3f21456eeb85")
true
"""
def is_uuid?(nil), do: false
def is_uuid?(string),
do: string =~ ~r/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/
def is_uuid_or_nil?(nil), do: true
def is_uuid_or_nil?(string), do: is_uuid?(string)
# def nil_or_empty?(nil), do: true
# def nil_or_empty?(str) when is_string(str), do: "" == str |> String.trim()
@doc """
Checks if the passed item is nil or empty string.
The param will be passed to `to_string()`
and then `String.trim()` and checked for empty string
## Examples
iex> CloudflareApi.Utils.nil_or_empty?("hello")
false
iex> CloudflareApi.Utils.nil_or_empty?("")
true
iex> CloudflareApi.Utils.nil_or_empty?(nil)
true
"""
def nil_or_empty?(str_or_nil) do
"" == str_or_nil |> to_string() |> String.trim()
end
@doc """
if `value` (value of the argument) is nil, this will raise `CloudflareApi.CantBeNil`
`argn` (name of the argument) will be passed to allow for more helpful error
messages that tell you the name of the variable that was `nil`
## Examples
iex> CloudflareApi.Utils.raise_if_nil!("somevar", "someval")
"someval"
iex> CloudflareApi.Utils.raise_if_nil!("somevar", nil)
** (CloudflareApi.CantBeNil) variable 'somevar' was nil but cannot be
(malan 0.1.0) lib/malan/utils.ex:135: CloudflareApi.Utils.raise_if_nil!/2
"""
def raise_if_nil!(varname, value) do
case is_nil(value) do
true -> raise CloudflareApi.CantBeNil, varname: varname
false -> value
end
end
@doc """
if `value` (value of the argument) is nil, this will raise `CloudflareApi.CantBeNil`
`argn` (name of the argument) will be passed to allow for more helpful error
messages that tell you the name of the variable that was `nil`
## Examples
iex> CloudflareApi.Utils.raise_if_nil!("someval")
"someval"
iex> CloudflareApi.Utils.raise_if_nil!(nil)
** (CloudflareApi.CantBeNil) variable 'somevar' was nil but cannot be
(malan 0.1.0) lib/malan/utils.ex:142: CloudflareApi.Utils.raise_if_nil!/1
"""
def raise_if_nil!(value) do
case is_nil(value) do
true -> raise CloudflareApi.CantBeNil
false -> value
end
end
@doc """
Convert a map to a `String`, suitable for printing.
Optionally pass a list of keys to mask.
## Examples
iex> map_to_string(%{michael: "knight"})
"michael: 'knight'"
iex> map_to_string(%{michael: "knight", kitt: "karr"})
"kitt: 'karr', michael: 'knight'"
iex> map_to_string(%{michael: "knight", kitt: "karr"}, [:kitt])
"kitt: '****', michael: 'knight'"
iex> map_to_string(%{michael: "knight", kitt: "karr"}, [:kitt, :michael])
"kitt: '****', michael: '******'"
iex> map_to_string(%{"michael" => "knight", "kitt" => "karr", "carr" => "hart"}, ["kitt", "michael"])
"carr: 'hart', kitt: '****', michael: '******'"
"""
def map_to_string(map, mask_keys \\ []) do
Map.to_list(map)
|> Enum.map(fn {key, val} ->
case key in list_to_strings_and_atoms(mask_keys) do
true -> {key, String.replace(val, ~r/./, "*")}
_ -> {key, val}
end
end)
|> Enum.map(fn {key, val} -> "#{key}: '#{val}'" end)
|> Enum.join(", ")
end
defp atom_or_string_to_string_or_atom(atom) when is_atom(atom) do
Atom.to_string(atom)
end
defp atom_or_string_to_string_or_atom(string) when is_binary(string) do
String.to_atom(string)
end
@doc """
Takes a list of strings or atoms and returns a list with string and atoms.
## Examples
iex> list_to_strings_and_atoms([:circle])
[:circle, "circle"]
iex> list_to_strings_and_atoms([:circle, :square])
[:square, "square", :circle, "circle"]
iex> list_to_strings_and_atoms(["circle", "square"])
["square", :square, "circle", :circle]
"""
def list_to_strings_and_atoms(list) do
Enum.reduce(list, [], fn l, acc -> [l | [atom_or_string_to_string_or_atom(l) | acc]] end)
end
end
defmodule CloudflareApi.Utils.Enum do
@doc """
will return true if all invocations of the function return false. If one callback returns `true`, the end result will be `false`
`Enum.all?` will return true if all invocations of the function return
true. `CloudflareApi.Utils.Enum.none?` is the opposite.
"""
def none?(enum, func) do
Enum.all?(enum, fn i -> !func.(i) end)
end
end
defmodule CloudflareApi.Utils.Crypto do
def strong_random_string(length) do
:crypto.strong_rand_bytes(length)
|> Base.encode64(padding: false)
|> String.replace(~r{\+}, "C")
|> String.replace(~r{/}, "z")
|> binary_part(0, length)
end
def hash_token(api_token) do
:crypto.hash(:sha256, api_token)
|> Base.encode64()
end
end
defmodule CloudflareApi.Utils.DateTime do
def utc_now_trunc(),
do: DateTime.truncate(DateTime.utc_now(), :second)
@doc "Return a DateTime about 200 years into the future"
def distant_future() do
round(52.5 * 200 * 7 * 24 * 60 * 60)
|> adjust_cur_time_trunc(:seconds)
end
# New implementation, needs testing
# def distant_future(),
# do: adjust_cur_time(200, :years)
@doc """
Add the specified number of units to the current time.
Supplying a negative number will adjust the time backwards by the
specified units, while supplying a positive will adjust the time
forwards by the specified units.
"""
def adjust_cur_time(num_years, :years),
do: adjust_cur_time(round(num_years * 52.5), :weeks)
def adjust_cur_time(num_weeks, :weeks),
do: adjust_cur_time(num_weeks * 7, :days)
def adjust_cur_time(num_days, :days),
do: adjust_cur_time(num_days * 24, :hours)
def adjust_cur_time(num_hours, :hours),
do: adjust_cur_time(num_hours * 60, :minutes)
def adjust_cur_time(num_minutes, :minutes),
do: adjust_cur_time(num_minutes * 60, :seconds)
def adjust_cur_time(num_seconds, :seconds),
do: adjust_time(DateTime.utc_now(), num_seconds, :seconds)
def adjust_cur_time_trunc(num_weeks, :weeks),
do: adjust_cur_time_trunc(num_weeks * 7, :days)
def adjust_cur_time_trunc(num_days, :days),
do: adjust_cur_time_trunc(num_days * 24, :hours)
def adjust_cur_time_trunc(num_hours, :hours),
do: adjust_cur_time_trunc(num_hours * 60, :minutes)
def adjust_cur_time_trunc(num_minutes, :minutes),
do: adjust_cur_time_trunc(num_minutes * 60, :seconds)
def adjust_cur_time_trunc(num_seconds, :seconds),
do: adjust_time(utc_now_trunc(), num_seconds, :seconds)
def adjust_time(time, num_weeks, :weeks),
do: adjust_time(time, num_weeks * 7, :days)
def adjust_time(time, num_days, :days),
do: adjust_time(time, num_days * 24, :hours)
def adjust_time(time, num_hours, :hours),
do: adjust_time(time, num_hours * 60, :minutes)
def adjust_time(time, num_minutes, :minutes),
do: adjust_time(time, num_minutes * 60, :seconds)
def adjust_time(time, num_seconds, :seconds),
do: DateTime.add(time, num_seconds, :second)
@doc "Check if `past_time` occurs before `current_time`. Equal date returns true"
@spec in_the_past?(DateTime.t(), DateTime.t()) :: boolean()
def in_the_past?(past_time, current_time),
do: DateTime.compare(past_time, current_time) != :gt
@doc "Check if `past_time` occurs before the current time"
@spec in_the_past?(DateTime.t()) :: boolean()
def in_the_past?(nil),
do: raise(ArgumentError, message: "past_time time must not be nil!")
def in_the_past?(past_time),
do: in_the_past?(past_time, DateTime.utc_now())
def expired?(expires_at, current_time),
do: in_the_past?(expires_at, current_time)
def expired?(nil),
do: raise(ArgumentError, message: "expires_at time must not be nil!")
def expired?(expires_at),
do: in_the_past?(expires_at, DateTime.utc_now())
end