defmodule Auth0.Common.Util do @moduledoc """ Documentation for Util. """ import Bitwise @doc """ Generate SHA256 hash value. ## Examples iex> Auth0.Common.Util.hash("erin") <<124, 188, 203, 12, 76, 170, 223, 159, 205, 181, 30, 228, 87, 168, 40, 204, 114, 164, 88, 121, 131, 27, 91, 151, 138, 226, 226, 206, 252, 68, 151, 5>> """ @spec hash(String.t()) :: binary def hash(str \\ "") do :crypto.hash(:sha256, str) end @doc """ Encode to Base 16 value with lower case. ## Examples iex> Auth0.Common.Util.hex_encode("erin") "6572696e" """ @spec hex_encode(String.t()) :: String.t() def hex_encode(str \\ "") do str |> Base.encode16(case: :lower) end @doc """ Generate HMAC-SHA256 hash value. ## Examples iex> Auth0.Common.Util.hmac("secret", "erin") <<66, 116, 70, 47, 123, 250, 150, 31, 51, 11, 16, 118, 149, 254, 106, 254, 89, 65, 153, 52, 185, 191, 44, 50, 250, 254, 55, 172, 101, 31, 249, 250>> """ @spec hmac(String.t(), String.t()) :: binary def hmac(secret, str) do :crypto.mac(:hmac, :sha256, secret, str) end @doc """ Generate UUID4 value. """ @spec uuid() :: String.t() def uuid() do <> = :crypto.strong_rand_bytes(16) (b1 <> <<(x &&& 0x0F) ||| 0x40>> <> b2 <> <<(y &&& 0x3F) ||| 0x80>> <> b3) |> Base.encode16() end @doc """ Remove nil value from map recursively. ## Examples iex> Auth0.Common.Util.remove_nil(%{a: 1, b: nil, c: 3}) %{a: 1, c: 3} iex> Auth0.Common.Util.remove_nil(%{:a => 1, :b => nil, :c => 3, "d" => nil, "f" => 5}) %{:a => 1, :c => 3, "f" => 5} """ @spec remove_nil(map) :: map def remove_nil(%{} = map) do map |> Enum.reject(fn {_, v} -> is_nil(v) end) |> Enum.map(fn {k, v} -> {k, case v do v when v |> is_map -> remove_nil(v) v when v |> is_list -> v |> Enum.map(fn v2 -> if(v2 |> is_map, do: remove_nil(v2), else: v2) end) v -> v end} end) |> Map.new() end @doc """ append query to endpoint. ## Examples iex> Auth0.Common.Util.append_query("a=1&c=3&d=erin%20the%20black", "/hoge/fuga") "/hoge/fuga?a=1&c=3&d=erin%20the%20black" """ @spec append_query(String.t(), String.t()) :: String.t() def append_query(query, endpoint) do case query do "" -> endpoint query -> "#{endpoint}?#{query}" end end @doc """ Convert to query from map. Nil value is removed. ## Examples iex> Auth0.Common.Util.encode_query(%{a: 1, b: nil, c: 3, d: "erin the black"}) "a=1&c=3&d=erin%20the%20black" """ @spec convert_to_query(map) :: String.t() def convert_to_query(%{} = map) do map |> remove_nil |> URI.encode_query(:rfc3986) end @doc """ Convert to form body from map. Nil value is removed. ## Examples iex> Auth0.Common.Util.encode_form_body(%{a: 1, b: nil, c: 3, d: "erin the black"}) {:form, [a: 1, b: nil, c: 3, d: "erin the black"]} """ @spec convert_to_form_body(map) :: {:form, keyword} def convert_to_form_body(%{} = map) do {:form, map |> remove_nil |> Map.to_list()} end @doc """ Convert struct to map recursively. """ @spec to_map(struct) :: map def to_map(struct) do if(struct |> is_struct, do: struct |> Map.from_struct(), else: struct) |> Enum.map(fn {k, v} -> {k, case v do v when v |> is_map -> to_map(v) v when v |> is_list -> v |> Enum.map(fn v2 -> if(v2 |> is_map, do: to_map(v2), else: v2) end) v -> v end} end) |> Map.new() end @doc """ Convert map to struct and string keys are considered. """ @spec to_struct(map, module) :: struct def to_struct(map, module) do struct( module, map |> Map.new(fn {k, v} -> {if(k |> is_atom, do: k, else: String.to_atom(k)), v} end) ) end @doc """ Decode json using atom key. """ @spec decode_json!(String.t()) :: map | list def decode_json!(value) do value |> Jason.decode!() |> key_to_atom() end defp key_to_atom(value) when value |> is_list do value |> Enum.map(&key_to_atom/1) end defp key_to_atom(value) when value |> is_map do value |> Enum.map(fn {k, v} -> { if(k |> is_atom, do: k, else: String.to_atom(k)), v |> key_to_atom() } end) |> Map.new() end defp key_to_atom(value), do: value end