Packages
unleash_fresha
1.13.0
5.1.4
5.1.3
5.1.2
5.1.1
5.1.0
5.0.0
4.0.0
4.0.0-git-bda3
3.0.1
3.0.0
2.1.0
2.0.1
2.0.0-git-75d8
2.0.0-git-67b2
2.0.0-git-4b61
2.0.0-git-49f4
1.16.0
1.15.0
1.14.1-git-e754
1.14.0
1.14.0-git-71e9
1.13.0
1.13.0-git-4ba6
1.12.0
1.11.0
1.11.0-git-1b62
1.10.2
1.10.2-git-b835
1.10.1
1.10.0
1.9.3-alpha0
1.9.2
1.9.1
1.9.0
An Unleash Feature Flag client for Elixir, forked from [unleash](https://gitlab.com/afontaine/unleash_ex)
Current section
Files
Jump to
Current section
Files
lib/propagation/serialization.ex
defmodule Unleash.Propagation.Serialization do
@moduledoc """
Module handling the (de)serialization of Unleash objects for propagation.
See `Unleash.Propagation`.
"""
defmodule ContextDeserializationError do
@moduledoc false
defexception [:message, :details]
@type t :: %__MODULE__{message: String.t(), details: term()}
end
defmodule OverridesDeserializationError do
@moduledoc false
defexception [:message, :details]
@type t :: %__MODULE__{message: String.t(), details: term()}
end
@propagated_context_schema %{
"userId" => :string,
"sessionId" => :string,
"properties" => {:custom, &__MODULE__.validate_context_properties/1}
}
@spec deserialize_context(binary()) :: {:ok, Unleash.context()} | {:error, ContextDeserializationError.t()}
def deserialize_context(serialized_context) do
with {:ok, payload} <- Jason.decode(serialized_context),
{:ok, validated} <- Peri.validate(@propagated_context_schema, payload) do
context =
Map.reject(
%{
user_id: validated["userId"],
session_id: validated["sessionId"],
properties: validated["properties"]
},
fn {_key, val} -> is_nil(val) end
)
{:ok, context}
else
{:error, %Jason.DecodeError{} = jason_error} ->
{:error, %ContextDeserializationError{message: "invalid JSON", details: jason_error}}
{:error, [%Peri.Error{} | _]} = peri_errors ->
{:error, %ContextDeserializationError{message: "invalid context object", details: peri_errors}}
end
end
def deserialize_context!(serialized_context) do
case deserialize_context(serialized_context) do
{:ok, context} -> context
{:error, exception} -> raise exception
end
end
@propagated_overrides_schema %{
"name" => {:required, :string},
"enabled" => {:required, {:either, {:boolean, :string}}}
}
@spec deserialize_overrides(binary()) :: {:ok, Unleash.context()} | {:error, OverridesDeserializationError.t()}
def deserialize_overrides(serialized_overrides) do
with {:ok, payload} <- Jason.decode(serialized_overrides),
{_tag, payload} when is_list(payload) <- {:check_list, payload},
{:ok, overrides} <- parse_overrides(payload) do
{:ok, overrides}
else
{:error, %Jason.DecodeError{} = jason_error} ->
{:error, %OverridesDeserializationError{message: "invalid JSON", details: jason_error}}
{:check_list, _not_a_list} ->
{:error, %OverridesDeserializationError{message: "not a list of overrides objects"}}
{:error, [%Peri.Error{} | _] = peri_errors} ->
{:error, %OverridesDeserializationError{message: "invalid override object", details: peri_errors}}
end
end
def deserialize_overrides!(serialized_overrides) do
case deserialize_overrides(serialized_overrides) do
{:ok, overrides} -> overrides
{:error, exception} -> raise exception
end
end
@spec parse_overrides(list(term())) :: {:ok, Unleash.overrides()} | {:error, Peri.Error.t()}
defp parse_overrides(overrides_payload) do
overrides_payload
|> Enum.reduce_while(
%{},
fn override, acc ->
case Peri.validate(@propagated_overrides_schema, override) do
{:ok, %{"name" => name, "enabled" => enabled}} ->
state =
case enabled do
true -> :on
false -> :off
variant when is_binary(variant) -> variant
end
{:cont, Map.put(acc, name, state)}
{:error, error} ->
{:halt, {:error, error}}
end
end
)
|> case do
{:error, error} -> {:error, error}
overrides -> {:ok, overrides}
end
end
def validate_context_properties(properties) when is_map(properties) do
properties
|> Enum.all?(fn {key, val} -> is_binary(key) and is_binary(val) end)
|> case do
true -> :ok
false -> {:error, "properties should be a flat object with string values", []}
end
end
def validate_context_properties(_properties), do: {:error, "invalid properties", []}
end