Packages
unleash_fresha
1.14.1-git-e754
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/unleash/variant.ex
defmodule Unleash.Variant do
@moduledoc false
alias Unleash.Feature
alias Unleash.Strategy.Utils
@derive Jason.Encoder
defstruct name: "",
weight: 0,
payload: %{},
overrides: []
@type t :: %{enabled: boolean(), name: String.t(), payload: map()}
@type result :: %{
required(:enabled) => boolean(),
required(:name) => String.t(),
optional(:payload) => map()
}
def select_variant(%Feature{variants: variants} = feature, context) when is_list(variants) and variants != [] do
{variant, metadata} =
case Feature.enabled?(feature, context) do
{true, _} -> variants(feature, context)
_ -> {disabled(), %{reason: :feature_disabled}}
end
common_metadata = %{
seed: get_seed(context, feature),
variants: Enum.map(variants, &{&1.name, &1.weight})
}
{variant, Map.merge(metadata, common_metadata)}
end
def select_variant(_feature, _context), do: {disabled(), %{reason: :feature_has_no_variants}}
@doc """
Like `select_variant/2`, but assumes that the feature is enabled
"""
def select_variant_assuming_feature_enabled(%Feature{variants: [_ | _]} = feature, context) do
{variant, metadata} = variants(feature, context)
common_metadata = %{
seed: get_seed(context, feature),
variants: Enum.map(feature.variants, &{&1.name, &1.weight})
}
{variant, Map.merge(metadata, common_metadata)}
end
def select_variant_assuming_feature_enabled(_feature, _context) do
{disabled(), %{reason: :feature_has_no_variants}}
end
def from_map(map) when is_map(map) do
%__MODULE__{
name: map["name"],
weight: map["weight"],
payload: map["payload"],
overrides: Map.get(map, "overrides", [])
}
end
def to_map(%__MODULE__{name: "disabled" = name}) do
%{
enabled: false,
name: name
}
end
def to_map(%__MODULE__{name: name, payload: payload}, enabled \\ false) do
%{
enabled: enabled,
name: name,
payload: payload
}
end
defp find_variant(variants, target) do
Enum.reduce_while(variants, 0, fn v, acc ->
case acc + v.weight do
x when x < target -> {:cont, x}
_ -> {:halt, v}
end
end)
end
defp find_override(variants, context) do
variants
|> Enum.filter(fn v -> check_variant_for_override(v, context) end)
|> Enum.at(0)
end
defp check_variant_for_override(%__MODULE__{overrides: []}, _context), do: false
defp check_variant_for_override(%__MODULE__{overrides: overrides}, context) do
Enum.any?(overrides, fn %{"contextName" => name, "values" => values} ->
Enum.any?(values, fn v -> v === Unleash.find_in_context(context, name) end)
end)
end
defp get_seed(context, feature) do
with nil <- Map.get(context, :session_id),
nil <- Map.get(context, :user_id),
nil <- Map.get(context, :remote_address) do
case get_in(feature.strategies, [Access.at(0), "parameters", "stickiness"]) do
nil -> to_string(:rand.uniform(100_000))
custom_name -> Unleash.find_in_context(context, custom_name)
end
end
end
def disabled do
%{
enabled: false,
name: "disabled"
}
end
defp variants(%Feature{variants: variants} = feature, context) when is_list(variants) and variants != [] do
total_weight =
for %{weight: weight} <- variants, reduce: 0 do
acc -> weight + acc
end
variants
|> find_override(context)
|> case do
nil ->
seed = get_seed(context, feature)
normalized_variant = Utils.normalize_variant(seed, feature.name, total_weight)
variant = find_variant(variants, normalized_variant)
{to_map(variant, true), %{reason: :variant_selected}}
variant ->
{to_map(variant, true), %{reason: :override_found}}
end
end
end