From ead514d7c0b9f9d06447d963e35a4eca48157088 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Mon, 8 Jan 2024 18:05:48 -0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Encapsulate=20config=20for?= =?UTF-8?q?mat=20(#114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ Rename settings -> feature flags This is a start at encapsulating the format of the Config map. * ♻️ Introduce Config.Preferences module Refactor existing use of preferences to use new module. * ♻️ Encapsulate EvaluationFormula and rules * ♻️ Move entry matching to config modules --- lib/config_cat/cache.ex | 4 +- lib/config_cat/cache_policy/auto.ex | 6 +- lib/config_cat/cache_policy/behaviour.ex | 2 +- lib/config_cat/cache_policy/helpers.ex | 20 ++---- lib/config_cat/cache_policy/lazy.ex | 2 +- lib/config_cat/cache_policy/manual.ex | 2 +- lib/config_cat/client.ex | 52 ++++++--------- lib/config_cat/config.ex | 49 +++++++------- lib/config_cat/config/evaluation_formula.ex | 70 ++++++++++++++++++++ lib/config_cat/config/percentage_rule.ex | 32 +++++++++ lib/config_cat/config/preferences.ex | 29 ++++++++ lib/config_cat/config/rollout_rule.ex | 61 +++++++++++++++++ lib/config_cat/config_fetcher.ex | 7 +- lib/config_cat/constants.ex | 8 --- lib/config_cat/hooks.ex | 8 +-- lib/config_cat/local_file_data_source.ex | 34 +++++----- lib/config_cat/local_map_data_source.ex | 15 ++--- lib/config_cat/null_data_source.ex | 2 +- lib/config_cat/override_data_source.ex | 2 +- lib/config_cat/rollout.ex | 42 ++++++------ test/config_cat/cache_policy/auto_test.exs | 70 +++++++++++--------- test/config_cat/cache_policy/lazy_test.exs | 24 +++---- test/config_cat/cache_policy/manual_test.exs | 8 +-- test/config_cat/cache_test.exs | 2 +- test/config_cat/data_governance_test.exs | 7 +- test/config_cat/default_user_test.exs | 4 +- test/config_cat/hooks_test.exs | 27 ++++---- test/config_cat_test.exs | 25 ++++--- test/flag_override_test.exs | 4 +- test/support/cache_policy_case.ex | 20 +++--- test/support/client_case.ex | 7 +- 31 files changed, 417 insertions(+), 228 deletions(-) create mode 100644 lib/config_cat/config/evaluation_formula.ex create mode 100644 lib/config_cat/config/percentage_rule.ex create mode 100644 lib/config_cat/config/preferences.ex create mode 100644 lib/config_cat/config/rollout_rule.ex diff --git a/lib/config_cat/cache.ex b/lib/config_cat/cache.ex index 573dae7d..9ba86769 100644 --- a/lib/config_cat/cache.ex +++ b/lib/config_cat/cache.ex @@ -83,8 +83,8 @@ defmodule ConfigCat.Cache do def handle_call(:get, _from, %State{latest_entry: nil} = state) do with {:ok, serialized} <- state.cache.get(state.cache_key), {:ok, entry} <- deserialize(serialized), - {:ok, settings} <- Config.fetch_settings(entry.config) do - Hooks.invoke_on_config_changed(state.instance_id, settings) + {:ok, feature_flags} <- Config.fetch_feature_flags(entry.config) do + Hooks.invoke_on_config_changed(state.instance_id, feature_flags) {:reply, {:ok, entry}, State.with_entry(state, entry)} else error -> diff --git a/lib/config_cat/cache_policy/auto.ex b/lib/config_cat/cache_policy/auto.ex index 128b8fa2..dfc29292 100644 --- a/lib/config_cat/cache_policy/auto.ex +++ b/lib/config_cat/cache_policy/auto.ex @@ -140,7 +140,7 @@ defmodule ConfigCat.CachePolicy.Auto do @impl GenServer def handle_call(:get, _from, %State{} = state) do - {:reply, Helpers.cached_settings(state), state} + {:reply, Helpers.cached_feature_flags(state), state} end @impl GenServer @@ -204,10 +204,10 @@ defmodule ConfigCat.CachePolicy.Auto do defp be_initialized(%State{} = state) when initialized?(state), do: state defp be_initialized(%State{} = state) do - settings = Helpers.cached_settings(state) + feature_flags = Helpers.cached_feature_flags(state) for caller <- state.policy_state.callers do - GenServer.reply(caller, settings) + GenServer.reply(caller, feature_flags) end Helpers.on_client_ready(state) diff --git a/lib/config_cat/cache_policy/behaviour.ex b/lib/config_cat/cache_policy/behaviour.ex index b1633e9c..758369f7 100644 --- a/lib/config_cat/cache_policy/behaviour.ex +++ b/lib/config_cat/cache_policy/behaviour.ex @@ -7,7 +7,7 @@ defmodule ConfigCat.CachePolicy.Behaviour do alias ConfigCat.FetchTime @callback get(ConfigCat.instance_id()) :: - {:ok, Config.settings(), FetchTime.t()} | {:error, :not_found} + {:ok, Config.feature_flags(), FetchTime.t()} | {:error, :not_found} @callback offline?(ConfigCat.instance_id()) :: boolean() @callback set_offline(ConfigCat.instance_id()) :: :ok @callback set_online(ConfigCat.instance_id()) :: :ok diff --git a/lib/config_cat/cache_policy/helpers.ex b/lib/config_cat/cache_policy/helpers.ex index 97679fa8..3cbf0eae 100644 --- a/lib/config_cat/cache_policy/helpers.ex +++ b/lib/config_cat/cache_policy/helpers.ex @@ -66,18 +66,12 @@ defmodule ConfigCat.CachePolicy.Helpers do Hooks.invoke_on_client_ready(state.instance_id) end - @spec cached_settings(State.t()) :: - {:ok, Config.settings(), FetchTime.t()} | {:error, :not_found} - def cached_settings(%State{} = state) do + @spec cached_feature_flags(State.t()) :: + {:ok, Config.feature_flags(), FetchTime.t()} | {:error, :not_found} + def cached_feature_flags(%State{} = state) do with {:ok, %ConfigEntry{} = entry} <- cached_entry(state), - {:ok, settings} <- Config.fetch_settings(entry.config) do - {:ok, settings, entry.fetch_time_ms} - else - :error -> - {:error, :not_found} - - error -> - error + {:ok, feature_flags} <- Config.fetch_feature_flags(entry.config) do + {:ok, feature_flags, entry.fetch_time_ms} end end @@ -111,8 +105,8 @@ defmodule ConfigCat.CachePolicy.Helpers do {:ok, %ConfigEntry{} = entry} -> update_cache(state, entry) - with {:ok, settings} <- Config.fetch_settings(entry.config) do - Hooks.invoke_on_config_changed(state.instance_id, settings) + with {:ok, feature_flags} <- Config.fetch_feature_flags(entry.config) do + Hooks.invoke_on_config_changed(state.instance_id, feature_flags) end :ok diff --git a/lib/config_cat/cache_policy/lazy.ex b/lib/config_cat/cache_policy/lazy.ex index a0476a41..b84c6535 100644 --- a/lib/config_cat/cache_policy/lazy.ex +++ b/lib/config_cat/cache_policy/lazy.ex @@ -41,7 +41,7 @@ defmodule ConfigCat.CachePolicy.Lazy do @impl GenServer def handle_call(:get, _from, %State{} = state) do with {:ok, new_state} <- maybe_refresh(state) do - {:reply, Helpers.cached_settings(new_state), new_state} + {:reply, Helpers.cached_feature_flags(new_state), new_state} end end diff --git a/lib/config_cat/cache_policy/manual.ex b/lib/config_cat/cache_policy/manual.ex index 84a93eb7..2d791859 100644 --- a/lib/config_cat/cache_policy/manual.ex +++ b/lib/config_cat/cache_policy/manual.ex @@ -33,7 +33,7 @@ defmodule ConfigCat.CachePolicy.Manual do @impl GenServer def handle_call(:get, _from, %State{} = state) do - {:reply, Helpers.cached_settings(state), state} + {:reply, Helpers.cached_feature_flags(state), state} end @impl GenServer diff --git a/lib/config_cat/client.ex b/lib/config_cat/client.ex index 2b938920..8e7e87b1 100644 --- a/lib/config_cat/client.ex +++ b/lib/config_cat/client.ex @@ -4,6 +4,7 @@ defmodule ConfigCat.Client do use GenServer alias ConfigCat.CachePolicy + alias ConfigCat.Config.EvaluationFormula alias ConfigCat.EvaluationDetails alias ConfigCat.FetchTime alias ConfigCat.Hooks @@ -12,7 +13,6 @@ defmodule ConfigCat.Client do alias ConfigCat.User require ConfigCat.ConfigCatLogger, as: ConfigCatLogger - require ConfigCat.Constants, as: Constants defmodule State do @moduledoc false @@ -96,9 +96,9 @@ defmodule ConfigCat.Client do @impl GenServer def handle_call({:get_key_and_value, variation_id}, _from, %State{} = state) do - case cached_settings(state) do - {:ok, settings, _fetch_time_ms} -> - result = Enum.find_value(settings, nil, &entry_matching(&1, variation_id)) + case cached_feature_flags(state) do + {:ok, feature_flags, _fetch_time_ms} -> + result = Enum.find_value(feature_flags, nil, &entry_matching(&1, variation_id)) if is_nil(result) do ConfigCatLogger.error( @@ -183,9 +183,9 @@ defmodule ConfigCat.Client do end defp do_get_all_keys(%State{} = state) do - case cached_settings(state) do - {:ok, settings, _fetch_time_ms} -> - Map.keys(settings) + case cached_feature_flags(state) do + {:ok, feature_flags, _fetch_time_ms} -> + Map.keys(feature_flags) _ -> ConfigCatLogger.error("Config JSON is not present. Returning empty result.", @@ -196,19 +196,10 @@ defmodule ConfigCat.Client do end end - defp entry_matching({key, setting}, variation_id) do - value_matching(key, setting, variation_id) || - value_matching(key, Map.get(setting, Constants.rollout_rules()), variation_id) || - value_matching(key, Map.get(setting, Constants.percentage_rules()), variation_id) - end - - defp value_matching(key, value, variation_id) when is_list(value) do - Enum.find_value(value, nil, &value_matching(key, &1, variation_id)) - end - - defp value_matching(key, value, variation_id) do - if Map.get(value, Constants.variation_id(), nil) == variation_id do - {key, Map.get(value, Constants.value())} + defp entry_matching({key, formula}, variation_id) do + case EvaluationFormula.variation_value(formula, variation_id) do + nil -> nil + value -> {key, value} end end @@ -216,10 +207,11 @@ defmodule ConfigCat.Client do user = if user != nil, do: user, else: state.default_user details = - case cached_settings(state) do - {:ok, settings, fetch_time_ms} -> + case cached_feature_flags(state) do + {:ok, feature_flags, fetch_time_ms} -> %EvaluationDetails{} = - details = Rollout.evaluate(key, user, default_value, default_variation_id, settings) + details = + Rollout.evaluate(key, user, default_value, default_variation_id, feature_flags) fetch_time = case FetchTime.to_datetime(fetch_time_ms) do @@ -249,22 +241,22 @@ defmodule ConfigCat.Client do details end - defp cached_settings(%State{} = state) do + defp cached_feature_flags(%State{} = state) do %{cache_policy: policy, flag_overrides: flag_overrides, instance_id: instance_id} = state - local_settings = OverrideDataSource.overrides(flag_overrides) + local_feature_flags = OverrideDataSource.overrides(flag_overrides) case OverrideDataSource.behaviour(flag_overrides) do :local_only -> - {:ok, local_settings, 0} + {:ok, local_feature_flags, 0} :local_over_remote -> - with {:ok, remote_settings, fetch_time_ms} <- policy.get(instance_id) do - {:ok, Map.merge(remote_settings, local_settings), fetch_time_ms} + with {:ok, remote_feature_flags, fetch_time_ms} <- policy.get(instance_id) do + {:ok, Map.merge(remote_feature_flags, local_feature_flags), fetch_time_ms} end :remote_over_local -> - with {:ok, remote_settings, fetch_time_ms} <- policy.get(instance_id) do - {:ok, Map.merge(local_settings, remote_settings), fetch_time_ms} + with {:ok, remote_feature_flags, fetch_time_ms} <- policy.get(instance_id) do + {:ok, Map.merge(local_feature_flags, remote_feature_flags), fetch_time_ms} end end end diff --git a/lib/config_cat/config.ex b/lib/config_cat/config.ex index 475e0c4c..898dd348 100644 --- a/lib/config_cat/config.ex +++ b/lib/config_cat/config.ex @@ -2,19 +2,23 @@ defmodule ConfigCat.Config do @moduledoc """ Defines configuration-related types used in the rest of the library. """ - alias ConfigCat.RedirectMode + alias ConfigCat.Config.EvaluationFormula + alias ConfigCat.Config.Preferences @typedoc false @type comparator :: non_neg_integer() + @typedoc false + @type feature_flags :: %{String.t() => EvaluationFormula.t()} + @typedoc "The name of a configuration setting." @type key :: String.t() - @typedoc "The configuration settings within a Config." - @type settings :: map() + @typedoc false + @type opt :: {:feature_flags, feature_flags()} | {:preferences, Preferences.t()} - @typedoc "A collection of configuration settings and preferences." - @type t :: map() + @typedoc "A collection of feature flags and preferences." + @type t :: %{String.t() => map()} @typedoc false @type url :: String.t() @@ -27,41 +31,34 @@ defmodule ConfigCat.Config do @feature_flags "f" @preferences "p" - @preferences_base_url "u" - @redirect_mode "r" @doc false - @spec new_with_preferences(url(), RedirectMode.t()) :: t() - def new_with_preferences(base_url, redirect_mode) do - %{ - @preferences => %{ - @preferences_base_url => base_url, - @redirect_mode => redirect_mode - } - } + @spec new([opt]) :: t() + def new(opts \\ []) do + feature_flags = Keyword.get(opts, :feature_flags, %{}) + preferences = Keyword.get_lazy(opts, :preferences, &Preferences.new/0) + + %{@feature_flags => feature_flags, @preferences => preferences} end @doc false - @spec new_with_settings(settings()) :: t() - def new_with_settings(settings) do - %{@feature_flags => settings} + @spec feature_flags(t()) :: feature_flags() + def feature_flags(config) do + Map.get(config, @feature_flags, %{}) end @doc false - @spec fetch_settings(t()) :: {:ok, settings()} | {:error, :not_found} - def fetch_settings(config) do + @spec fetch_feature_flags(t()) :: {:ok, feature_flags()} | {:error, :not_found} + def fetch_feature_flags(config) do case Map.fetch(config, @feature_flags) do - {:ok, settings} -> {:ok, settings} + {:ok, feature_flags} -> {:ok, feature_flags} :error -> {:error, :not_found} end end @doc false - @spec preferences(t()) :: {url() | nil, RedirectMode.t() | nil} + @spec preferences(t()) :: Preferences.t() def preferences(config) do - case config[@preferences] do - nil -> {nil, nil} - preferences -> {preferences[@preferences_base_url], preferences[@redirect_mode]} - end + Map.get_lazy(config, @preferences, &Preferences.new/0) end end diff --git a/lib/config_cat/config/evaluation_formula.ex b/lib/config_cat/config/evaluation_formula.ex new file mode 100644 index 00000000..6fa82a6b --- /dev/null +++ b/lib/config_cat/config/evaluation_formula.ex @@ -0,0 +1,70 @@ +defmodule ConfigCat.Config.EvaluationFormula do + @moduledoc false + alias ConfigCat.Config + alias ConfigCat.Config.PercentageRule + alias ConfigCat.Config.RolloutRule + + @type opt :: {:value, Config.value()} + @type t :: %{String.t() => term()} + + @percentage_rules "p" + @rollout_rules "r" + @value "v" + @variation_id "i" + + @spec new([opt]) :: t() + def new(opts \\ []) do + %{ + @value => opts[:value] + } + end + + @spec percentage_rules(t()) :: [PercentageRule.t()] + def percentage_rules(formula) do + Map.get(formula, @percentage_rules, []) + end + + @spec rollout_rules(t()) :: [RolloutRule.t()] + def rollout_rules(formula) do + Map.get(formula, @rollout_rules, []) + end + + @spec value(t()) :: Config.value() + @spec value(t(), Config.value() | nil) :: Config.value() | nil + def value(formula, default \\ nil) do + Map.get(formula, @value, default) + end + + @spec variation_id(t()) :: Config.variation_id() | nil + @spec variation_id(t(), Config.variation_id() | nil) :: Config.variation_id() | nil + def variation_id(formula, default \\ nil) do + Map.get(formula, @variation_id, default) + end + + @spec variation_value(t(), Config.variation_id()) :: Config.value() | nil + def variation_value(formula, variation_id) do + if variation_id(formula) == variation_id do + value(formula) + else + rollout_value = rollout_rule_variation_value(formula, variation_id) + + if is_nil(rollout_value) do + percentage_rule_variation_value(formula, variation_id) + else + rollout_value + end + end + end + + defp rollout_rule_variation_value(formula, variation_id) do + formula + |> rollout_rules() + |> Enum.find_value(nil, &RolloutRule.variation_value(&1, variation_id)) + end + + defp percentage_rule_variation_value(formula, variation_id) do + formula + |> percentage_rules() + |> Enum.find_value(nil, &PercentageRule.variation_value(&1, variation_id)) + end +end diff --git a/lib/config_cat/config/percentage_rule.ex b/lib/config_cat/config/percentage_rule.ex new file mode 100644 index 00000000..d96df8c5 --- /dev/null +++ b/lib/config_cat/config/percentage_rule.ex @@ -0,0 +1,32 @@ +defmodule ConfigCat.Config.PercentageRule do + @moduledoc false + alias ConfigCat.Config + + @type t :: %{String.t() => term()} + + @percentage "p" + @value "v" + @variation_id "i" + + @spec percentage(t()) :: non_neg_integer() + def percentage(rule) do + Map.get(rule, @percentage, 0) + end + + @spec value(t()) :: Config.value() + def value(rule) do + Map.get(rule, @value) + end + + @spec variation_id(t()) :: Config.variation_id() | nil + def variation_id(rule) do + Map.get(rule, @variation_id) + end + + @spec variation_value(t(), Config.variation_id()) :: Config.value() | nil + def variation_value(rule, variation_id) do + if variation_id(rule) == variation_id do + value(rule) + end + end +end diff --git a/lib/config_cat/config/preferences.ex b/lib/config_cat/config/preferences.ex new file mode 100644 index 00000000..17b7c8e4 --- /dev/null +++ b/lib/config_cat/config/preferences.ex @@ -0,0 +1,29 @@ +defmodule ConfigCat.Config.Preferences do + @moduledoc false + alias ConfigCat.RedirectMode + + @type opt :: {:base_url, url()} | {:redirect_mode, RedirectMode.t()} + @type t :: %{String.t() => term()} + @type url :: String.t() + + @base_url "u" + @redirect_mode "r" + + @spec new([opt]) :: t() + def new(opts \\ []) do + %{ + @base_url => opts[:base_url], + @redirect_mode => opts[:redirect_mode] + } + end + + @spec base_url(t()) :: url() | nil + def base_url(preferences) do + Map.get(preferences, @base_url) + end + + @spec redirect_mode(t()) :: RedirectMode.t() | nil + def redirect_mode(preferences) do + Map.get(preferences, @redirect_mode) + end +end diff --git a/lib/config_cat/config/rollout_rule.ex b/lib/config_cat/config/rollout_rule.ex new file mode 100644 index 00000000..2fdbf5dc --- /dev/null +++ b/lib/config_cat/config/rollout_rule.ex @@ -0,0 +1,61 @@ +defmodule ConfigCat.Config.RolloutRule do + @moduledoc false + alias ConfigCat.Config + + @type opt :: + {:comparator, Config.comparator()} + | {:comparison_attribute, String.t()} + | {:comparison_value, Config.value()} + | {:value, Config.value()} + | {:variation_id, Config.variation_id()} + @type t :: %{String.t() => term()} + + @comparator "t" + @comparison_attribute "a" + @comparison_value "c" + @value "v" + @variation_id "i" + + @spec new([opt]) :: t() + def new(opts \\ []) do + %{ + @comparator => opts[:comparator], + @comparison_attribute => opts[:comparison_attribute], + @comparison_value => opts[:comparison_value], + @value => opts[:value], + @variation_id => opts[:variation_id] + } + end + + @spec comparator(t()) :: Config.comparator() | nil + def comparator(rule) do + Map.get(rule, @comparator) + end + + @spec comparison_attribute(t()) :: String.t() | nil + def comparison_attribute(rule) do + Map.get(rule, @comparison_attribute) + end + + @spec comparison_value(t()) :: Config.value() | nil + def comparison_value(rule) do + Map.get(rule, @comparison_value) + end + + @spec value(t()) :: Config.value() + def value(rule) do + Map.get(rule, @value) + end + + @spec variation_id(t()) :: Config.variation_id() | nil + def variation_id(rule) do + Map.get(rule, @variation_id) + end + + @spec variation_value(t(), Config.variation_id()) :: Config.value() | nil + def variation_value(rule, variation_id) do + if variation_id(rule) == variation_id do + value(rule) + end + end +end diff --git a/lib/config_cat/config_fetcher.ex b/lib/config_cat/config_fetcher.ex index 90941a44..1970896e 100644 --- a/lib/config_cat/config_fetcher.ex +++ b/lib/config_cat/config_fetcher.ex @@ -39,6 +39,7 @@ defmodule ConfigCat.CacheControlConfigFetcher do use GenServer alias ConfigCat.Config + alias ConfigCat.Config.Preferences alias ConfigCat.ConfigEntry alias ConfigCat.ConfigFetcher alias ConfigCat.ConfigFetcher.FetchError @@ -228,9 +229,11 @@ defmodule ConfigCat.CacheControlConfigFetcher do with {:ok, config} <- Jason.decode(raw_config), new_etag = extract_etag(headers), %{base_url: new_base_url, custom_endpoint?: custom_endpoint?, redirects: redirects} <- - state, - {base_url, redirect_mode} <- Config.preferences(config) do + state do + preferences = Config.preferences(config) followed? = Map.has_key?(redirects, new_base_url) + base_url = Preferences.base_url(preferences) + redirect_mode = Preferences.redirect_mode(preferences) new_state = cond do diff --git a/lib/config_cat/constants.ex b/lib/config_cat/constants.ex index 45f95e3a..df62dabf 100644 --- a/lib/config_cat/constants.ex +++ b/lib/config_cat/constants.ex @@ -8,13 +8,5 @@ defmodule ConfigCat.Constants do defmacro config_filename, do: "config_v5.json" defmacro serialization_format_version, do: "v2" - defmacro comparator, do: "t" - defmacro comparison_attribute, do: "a" - defmacro comparison_value, do: "c" - defmacro rollout_rules, do: "r" - defmacro percentage_rules, do: "p" - defmacro percentage, do: "p" - defmacro value, do: "v" - defmacro variation_id, do: "i" defmacro fetch_timeout, do: 10_000 end diff --git a/lib/config_cat/hooks.ex b/lib/config_cat/hooks.ex index 9f09918d..e5fb47a2 100644 --- a/lib/config_cat/hooks.ex +++ b/lib/config_cat/hooks.ex @@ -78,7 +78,7 @@ defmodule ConfigCat.Hooks do """ @type named_callback :: {module(), atom(), list()} @type on_client_ready_callback :: (-> any()) | named_callback() - @type on_config_changed_callback :: (Config.settings() -> any()) | named_callback() + @type on_config_changed_callback :: (Config.feature_flags() -> any()) | named_callback() @type on_error_callback :: (String.t() -> any()) | named_callback() @type on_flag_evaluated_callback :: (EvaluationDetails.t() -> any()) | named_callback() @type option :: @@ -154,11 +154,11 @@ defmodule ConfigCat.Hooks do end @doc false - @spec invoke_on_config_changed(t(), Config.settings()) :: :ok - def invoke_on_config_changed(instance_id, settings) do + @spec invoke_on_config_changed(t(), Config.feature_flags()) :: :ok + def invoke_on_config_changed(instance_id, feature_flags) do instance_id |> hooks() - |> Impl.invoke_hook(:on_config_changed, [settings]) + |> Impl.invoke_hook(:on_config_changed, [feature_flags]) end @doc false diff --git a/lib/config_cat/local_file_data_source.ex b/lib/config_cat/local_file_data_source.ex index 468cd5b8..b4d539c1 100644 --- a/lib/config_cat/local_file_data_source.ex +++ b/lib/config_cat/local_file_data_source.ex @@ -19,7 +19,7 @@ defmodule ConfigCat.LocalFileDataSource do typedstruct do field :cached_timestamp, non_neg_integer(), default: 0 - field :settings, Config.settings() + field :feature_flags, Config.feature_flags() end @spec start_link(GenServer.options()) :: Agent.on_start() @@ -27,11 +27,11 @@ defmodule ConfigCat.LocalFileDataSource do Agent.start_link(fn -> %__MODULE__{} end) end - @spec cached_settings(Agent.agent()) :: {:ok, Config.t()} | {:error, :not_found} - def cached_settings(cache) do - case Agent.get(cache, fn %__MODULE__{settings: settings} -> settings end) do + @spec cached_feature_flags(Agent.agent()) :: {:ok, Config.t()} | {:error, :not_found} + def cached_feature_flags(cache) do + case Agent.get(cache, fn %__MODULE__{feature_flags: feature_flags} -> feature_flags end) do nil -> {:error, :not_found} - settings -> {:ok, settings} + feature_flags -> {:ok, feature_flags} end end @@ -41,9 +41,9 @@ defmodule ConfigCat.LocalFileDataSource do end @spec update(Agent.agent(), Config.t(), integer()) :: :ok - def update(cache, settings, timestamp) do + def update(cache, feature_flags, timestamp) do Agent.update(cache, fn %__MODULE__{} = state -> - %{state | cached_timestamp: timestamp, settings: settings} + %{state | cached_timestamp: timestamp, feature_flags: feature_flags} end) end end @@ -72,19 +72,18 @@ defmodule ConfigCat.LocalFileDataSource do end defimpl OverrideDataSource do + alias ConfigCat.Config.EvaluationFormula alias ConfigCat.LocalFileDataSource - require ConfigCat.Constants, as: Constants - @spec behaviour(LocalFileDataSource.t()) :: OverrideDataSource.behaviour() def behaviour(data_source), do: data_source.override_behaviour - @spec overrides(LocalFileDataSource.t()) :: Config.settings() + @spec overrides(LocalFileDataSource.t()) :: Config.feature_flags() def overrides(%{cache: cache} = data_source) do refresh_cache(cache, data_source.filename) - case FileCache.cached_settings(cache) do - {:ok, settings} -> settings + case FileCache.cached_feature_flags(cache) do + {:ok, feature_flags} -> feature_flags _ -> %{} end end @@ -94,8 +93,8 @@ defmodule ConfigCat.LocalFileDataSource do unless FileCache.cached_timestamp(cache) == timestamp do with {:ok, contents} <- File.read(filename), {:ok, data} <- Jason.decode(contents) do - settings = normalize(data) - FileCache.update(cache, settings, timestamp) + feature_flags = normalize(data) + FileCache.update(cache, feature_flags, timestamp) else error -> log_error(error, filename) @@ -121,15 +120,12 @@ defmodule ConfigCat.LocalFileDataSource do defp normalize(%{"flags" => source} = _data) do source - |> Enum.map(fn {key, value} -> {key, %{Constants.value() => value}} end) + |> Enum.map(fn {key, value} -> {key, EvaluationFormula.new(value: value)} end) |> Map.new() end defp normalize(source) do - case Config.fetch_settings(source) do - {:ok, settings} -> settings - _ -> %{} - end + Config.feature_flags(source) end end end diff --git a/lib/config_cat/local_map_data_source.ex b/lib/config_cat/local_map_data_source.ex index 53650624..e732c2dc 100644 --- a/lib/config_cat/local_map_data_source.ex +++ b/lib/config_cat/local_map_data_source.ex @@ -7,13 +7,12 @@ defmodule ConfigCat.LocalMapDataSource do use TypedStruct alias ConfigCat.Config + alias ConfigCat.Config.EvaluationFormula alias ConfigCat.OverrideDataSource - require ConfigCat.Constants, as: Constants - typedstruct enforce: true do field :override_behaviour, OverrideDataSource.behaviour() - field :settings, Config.settings() + field :feature_flags, Config.feature_flags() end @doc """ @@ -21,14 +20,14 @@ defmodule ConfigCat.LocalMapDataSource do """ @spec new(map, OverrideDataSource.behaviour()) :: t def new(overrides, override_behaviour) do - settings = + feature_flags = overrides - |> Enum.map(fn {key, value} -> {key, %{Constants.value() => value}} end) + |> Enum.map(fn {key, value} -> {key, EvaluationFormula.new(value: value)} end) |> Map.new() %__MODULE__{ override_behaviour: override_behaviour, - settings: settings + feature_flags: feature_flags } end @@ -38,7 +37,7 @@ defmodule ConfigCat.LocalMapDataSource do @spec behaviour(LocalMapDataSource.t()) :: OverrideDataSource.behaviour() def behaviour(%{override_behaviour: behaviour}), do: behaviour - @spec overrides(LocalMapDataSource.t()) :: Config.settings() - def overrides(%{settings: settings}), do: settings + @spec overrides(LocalMapDataSource.t()) :: Config.feature_flags() + def overrides(%{feature_flags: feature_flags}), do: feature_flags end end diff --git a/lib/config_cat/null_data_source.ex b/lib/config_cat/null_data_source.ex index 7a25b9db..a317c9ec 100644 --- a/lib/config_cat/null_data_source.ex +++ b/lib/config_cat/null_data_source.ex @@ -29,7 +29,7 @@ defmodule ConfigCat.NullDataSource do @spec behaviour(NullDataSource.t()) :: OverrideDataSource.behaviour() def behaviour(_data_source), do: :local_over_remote - @spec overrides(NullDataSource.t()) :: Config.settings() + @spec overrides(NullDataSource.t()) :: Config.feature_flags() def overrides(_data_source), do: %{} end end diff --git a/lib/config_cat/override_data_source.ex b/lib/config_cat/override_data_source.ex index 2826cc58..6af2175b 100644 --- a/lib/config_cat/override_data_source.ex +++ b/lib/config_cat/override_data_source.ex @@ -43,6 +43,6 @@ defprotocol ConfigCat.OverrideDataSource do @doc """ Return the local flag overrides from the data source. """ - @spec overrides(data_source :: t) :: Config.settings() + @spec overrides(data_source :: t) :: Config.feature_flags() def overrides(data_source) end diff --git a/lib/config_cat/rollout.ex b/lib/config_cat/rollout.ex index 816da33f..fd59d399 100644 --- a/lib/config_cat/rollout.ex +++ b/lib/config_cat/rollout.ex @@ -2,32 +2,34 @@ defmodule ConfigCat.Rollout do @moduledoc false alias ConfigCat.Config + alias ConfigCat.Config.EvaluationFormula + alias ConfigCat.Config.PercentageRule + alias ConfigCat.Config.RolloutRule alias ConfigCat.EvaluationDetails alias ConfigCat.Rollout.Comparator alias ConfigCat.User require ConfigCat.ConfigCatLogger, as: ConfigCatLogger - require ConfigCat.Constants, as: Constants @spec evaluate( Config.key(), User.t() | nil, Config.value(), Config.variation_id() | nil, - Config.settings() + Config.feature_flags() ) :: EvaluationDetails.t() - def evaluate(key, user, default_value, default_variation_id, settings) do + def evaluate(key, user, default_value, default_variation_id, feature_flags) do {:ok, logs} = Agent.start(fn -> [] end) try do log_evaluating(logs, key, user) with {:ok, valid_user} <- validate_user(user), - {:ok, setting_descriptor} <- setting_descriptor(settings, key, default_value), + {:ok, setting_descriptor} <- setting_descriptor(feature_flags, key, default_value), setting_variation = - Map.get(setting_descriptor, Constants.variation_id(), default_variation_id), - rollout_rules = Map.get(setting_descriptor, Constants.rollout_rules(), []), - percentage_rules = Map.get(setting_descriptor, Constants.percentage_rules(), []), + EvaluationFormula.variation_id(setting_descriptor, default_variation_id), + rollout_rules = EvaluationFormula.rollout_rules(setting_descriptor), + percentage_rules = EvaluationFormula.percentage_rules(setting_descriptor), {value, variation, rule, percentage_rule} <- evaluate_rules(rollout_rules, percentage_rules, valid_user, key, logs) do variation = variation || setting_variation @@ -50,7 +52,7 @@ defmodule ConfigCat.Rollout do else {:error, :invalid_user} -> log_invalid_user(key) - evaluate(key, nil, default_value, default_variation_id, settings) + evaluate(key, nil, default_value, default_variation_id, feature_flags) {:error, message} -> ConfigCatLogger.error(message, event_id: 1001) @@ -78,14 +80,14 @@ defmodule ConfigCat.Rollout do defp validate_user(%User{} = user), do: {:ok, user} defp validate_user(_), do: {:error, :invalid_user} - defp setting_descriptor(settings, key, default_value) do - case Map.fetch(settings, key) do + defp setting_descriptor(feature_flags, key, default_value) do + case Map.fetch(feature_flags, key) do {:ok, descriptor} -> {:ok, descriptor} :error -> available_keys = - settings + feature_flags |> Map.keys() |> Enum.map_join(", ", &"'#{&1}'") @@ -121,11 +123,11 @@ defmodule ConfigCat.Rollout do end defp evaluate_rollout_rule(rule, default, user, logs) do - comparison_attribute = Map.get(rule, Constants.comparison_attribute()) - comparison_value = Map.get(rule, Constants.comparison_value()) - comparator = Map.get(rule, Constants.comparator()) - value = Map.get(rule, Constants.value()) - variation = Map.get(rule, Constants.variation_id()) + comparison_attribute = RolloutRule.comparison_attribute(rule) + comparison_value = RolloutRule.comparison_value(rule) + comparator = RolloutRule.comparator(rule) + value = RolloutRule.value(rule) + variation = RolloutRule.variation_id(rule) case User.get_attribute(user, comparison_attribute) do nil -> @@ -182,8 +184,8 @@ defmodule ConfigCat.Rollout do bucket = increment_bucket(bucket, rule) if hash_val < bucket do - percentage_value = Map.get(rule, Constants.value()) - variation_value = Map.get(rule, Constants.variation_id()) + percentage_value = PercentageRule.value(rule) + variation_value = PercentageRule.variation_id(rule) {:halt, {percentage_value, variation_value, rule}} else @@ -191,7 +193,7 @@ defmodule ConfigCat.Rollout do end end - defp increment_bucket(bucket, rule), do: bucket + Map.get(rule, Constants.percentage(), 0) + defp increment_bucket(bucket, rule), do: bucket + PercentageRule.percentage(rule) defp hash_user(user, key) do user_key = User.get_attribute(user, "Identifier") @@ -208,7 +210,7 @@ defmodule ConfigCat.Rollout do end defp base_value(setting_descriptor, default_value, logs) do - result = Map.get(setting_descriptor, Constants.value(), default_value) + result = EvaluationFormula.value(setting_descriptor, default_value) log(logs, "Returning #{result}") result diff --git a/test/config_cat/cache_policy/auto_test.exs b/test/config_cat/cache_policy/auto_test.exs index d9b1b162..d0f10510 100644 --- a/test/config_cat/cache_policy/auto_test.exs +++ b/test/config_cat/cache_policy/auto_test.exs @@ -43,30 +43,33 @@ defmodule ConfigCat.CachePolicy.AutoTest do end describe "getting the config" do - test "refreshes automatically after initializing", %{entry: entry, settings: settings} do + test "refreshes automatically after initializing", %{ + entry: entry, + feature_flags: feature_flags + } do expect_refresh(entry) {:ok, instance_id} = start_cache_policy(@policy) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end test "skips initial fetch if cache is already populated with a recent entry", - %{entry: entry, settings: settings} do + %{entry: entry, feature_flags: feature_flags} do expect_not_refreshed() {:ok, instance_id} = start_cache_policy(@policy, initial_entry: entry) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end test "performs initial fetch if cache is already populated with an older entry", - %{entry: entry, settings: settings} do + %{entry: entry, feature_flags: feature_flags} do %{entry: old_entry} = make_old_entry(@policy.poll_interval_ms + 1) expect_refresh(entry) {:ok, instance_id} = start_cache_policy(@policy, initial_entry: old_entry) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end @tag capture_log: true @@ -75,7 +78,7 @@ defmodule ConfigCat.CachePolicy.AutoTest do wait_time_ms = 100 policy = CachePolicy.auto(max_init_wait_time_seconds: wait_time_ms / 1000.0) - %{entry: old_entry, settings: old_settings} = make_old_entry() + %{entry: old_entry, feature_flags: old_feature_flags} = make_old_entry() old_entry = Map.update!(old_entry, :fetch_time_ms, &(&1 - policy.poll_interval_ms - 1)) expect(MockFetcher, :fetch, fn _id, _etag -> @@ -86,7 +89,7 @@ defmodule ConfigCat.CachePolicy.AutoTest do {:ok, instance_id} = start_cache_policy(policy, initial_entry: old_entry) before = FetchTime.now_ms() - assert {:ok, old_settings, old_entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, old_feature_flags, old_entry.fetch_time_ms} == CachePolicy.get(instance_id) elapsed_ms = FetchTime.now_ms() - before assert wait_time_ms <= elapsed_ms && elapsed_ms <= wait_time_ms * 2 @@ -101,7 +104,10 @@ defmodule ConfigCat.CachePolicy.AutoTest do CachePolicy.get(instance_id) end - test "refreshes automatically after poll interval", %{entry: entry, settings: settings} do + test "refreshes automatically after poll interval", %{ + entry: entry, + feature_flags: feature_flags + } do %{entry: old_entry} = make_old_entry() expect_refresh(old_entry) @@ -113,27 +119,27 @@ defmodule ConfigCat.CachePolicy.AutoTest do expect_refresh(entry) wait_for_poll(policy) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end end describe "refreshing the config" do - test "stores new config in the cache", %{entry: entry, settings: settings} do - %{entry: old_entry, settings: old_settings} = make_old_entry() + test "stores new config in the cache", %{entry: entry, feature_flags: feature_flags} do + %{entry: old_entry, feature_flags: old_feature_flags} = make_old_entry() expect_refresh(old_entry) {:ok, instance_id} = start_cache_policy(@policy) - assert {:ok, old_settings, old_entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, old_feature_flags, old_entry.fetch_time_ms} == CachePolicy.get(instance_id) expect_refresh(entry) assert :ok = CachePolicy.force_refresh(instance_id) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end test "updates fetch time when server responds that the config hasn't changed", %{ entry: entry, - settings: settings + feature_flags: feature_flags } do entry = Map.update!(entry, :fetch_time_ms, &(&1 - 200)) @@ -147,7 +153,7 @@ defmodule ConfigCat.CachePolicy.AutoTest do assert :ok = CachePolicy.force_refresh(instance_id) - assert {:ok, ^settings, new_fetch_time_ms} = CachePolicy.get(instance_id) + assert {:ok, ^feature_flags, new_fetch_time_ms} = CachePolicy.get(instance_id) assert before <= new_fetch_time_ms && new_fetch_time_ms <= FetchTime.now_ms() end @@ -166,9 +172,9 @@ defmodule ConfigCat.CachePolicy.AutoTest do test_pid = self() instance_id = String.to_atom(UUID.uuid4()) - %{entry: old_entry, settings: old_settings} = make_old_entry() + %{entry: old_entry, feature_flags: old_feature_flags} = make_old_entry() - callback = fn settings -> send(test_pid, {:config_changed, settings}) end + callback = fn feature_flags -> send(test_pid, {:config_changed, feature_flags}) end start_supervised!({Hooks, hooks: [on_config_changed: callback], instance_id: instance_id}) @@ -181,7 +187,7 @@ defmodule ConfigCat.CachePolicy.AutoTest do ensure_initialized(instance_id) - assert_received {:config_changed, ^old_settings} + assert_received {:config_changed, ^old_feature_flags} %{instance_id: instance_id, policy: policy} end @@ -189,44 +195,48 @@ defmodule ConfigCat.CachePolicy.AutoTest do test "calls the change callback after polled refresh", %{ entry: entry, policy: policy, - settings: settings + feature_flags: feature_flags } do expect_refresh(entry) wait_for_poll(policy) - assert_receive {:config_changed, ^settings} + assert_receive {:config_changed, ^feature_flags} end test "doesn't call the change callback if the configuration hasn't changed", %{policy: policy} do expect_unchanged() wait_for_poll(policy) - refute_receive {:config_changed, _settings} + refute_receive {:config_changed, _feature_flags} end test "calls the change callback after forced refresh", %{ entry: entry, instance_id: instance_id, - settings: settings + feature_flags: feature_flags } do expect_refresh(entry) :ok = CachePolicy.force_refresh(instance_id) - assert_receive {:config_changed, ^settings} + assert_receive {:config_changed, ^feature_flags} end end describe "offline" do - test "does not fetch config when offline mode is set", %{entry: entry, settings: settings} do + test "does not fetch config when offline mode is set", %{ + entry: entry, + feature_flags: feature_flags + } do policy = CachePolicy.auto(poll_interval_seconds: 1) - %{entry: old_entry, settings: old_settings} = make_old_entry(policy.poll_interval_ms + 1) + %{entry: old_entry, feature_flags: old_feature_flags} = + make_old_entry(policy.poll_interval_ms + 1) expect_refresh(old_entry) {:ok, instance_id} = start_cache_policy(policy) refute CachePolicy.offline?(instance_id) - assert {:ok, old_settings, old_entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, old_feature_flags, old_entry.fetch_time_ms} == CachePolicy.get(instance_id) assert :ok = CachePolicy.set_offline(instance_id) assert CachePolicy.offline?(instance_id) @@ -234,7 +244,7 @@ defmodule ConfigCat.CachePolicy.AutoTest do expect_not_refreshed() wait_for_poll(policy) - assert {:ok, old_settings, old_entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, old_feature_flags, old_entry.fetch_time_ms} == CachePolicy.get(instance_id) expect_refresh(entry, self()) @@ -243,12 +253,12 @@ defmodule ConfigCat.CachePolicy.AutoTest do assert_receive :fetch_complete - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end end defp ensure_initialized(instance_id) do - _settings = CachePolicy.get(instance_id) + _feature_flags = CachePolicy.get(instance_id) end defp wait_for_poll(policy) do diff --git a/test/config_cat/cache_policy/lazy_test.exs b/test/config_cat/cache_policy/lazy_test.exs index c3b212dc..2d069bfa 100644 --- a/test/config_cat/cache_policy/lazy_test.exs +++ b/test/config_cat/cache_policy/lazy_test.exs @@ -21,30 +21,30 @@ defmodule ConfigCat.CachePolicy.LazyTest do end describe "getting the config" do - test "fetches config when first requested", %{entry: entry, settings: settings} do + test "fetches config when first requested", %{entry: entry, feature_flags: feature_flags} do {:ok, instance_id} = start_cache_policy(@policy) expect_refresh(entry) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end test "skips initial fetch if cache is already populated with a recent entry", - %{entry: entry, settings: settings} do + %{entry: entry, feature_flags: feature_flags} do {:ok, instance_id} = start_cache_policy(@policy, initial_entry: entry) expect_not_refreshed() - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end test "performs initial fetch if cache is already populated with an older entry", - %{entry: entry, settings: settings} do + %{entry: entry, feature_flags: feature_flags} do %{entry: old_entry} = make_old_entry(@policy.cache_refresh_interval_ms + 1) {:ok, instance_id} = start_cache_policy(@policy, initial_entry: old_entry) expect_refresh(entry) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end test "doesn't re-fetch if cache has not expired", %{entry: entry} do @@ -57,7 +57,7 @@ defmodule ConfigCat.CachePolicy.LazyTest do CachePolicy.get(instance_id) end - test "re-fetches if cache has expired", %{entry: entry, settings: settings} do + test "re-fetches if cache has expired", %{entry: entry, feature_flags: feature_flags} do policy = CachePolicy.lazy(cache_refresh_interval_seconds: 0) {:ok, instance_id} = start_cache_policy(policy) %{entry: old_entry} = make_old_entry() @@ -66,18 +66,18 @@ defmodule ConfigCat.CachePolicy.LazyTest do :ok = CachePolicy.force_refresh(instance_id) expect_refresh(entry) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end end describe "refreshing the config" do - test "stores new config in the cache", %{entry: entry, settings: settings} do + test "stores new config in the cache", %{entry: entry, feature_flags: feature_flags} do {:ok, instance_id} = start_cache_policy(@policy) expect_refresh(entry) assert :ok = CachePolicy.force_refresh(instance_id) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end test "fetches new config even if cache is not expired", %{entry: entry} do @@ -92,7 +92,7 @@ defmodule ConfigCat.CachePolicy.LazyTest do test "updates fetch time when server responds that the config hasn't changed", %{ entry: entry, - settings: settings + feature_flags: feature_flags } do entry = Map.update!(entry, :fetch_time_ms, &(&1 - 200)) {:ok, instance_id} = start_cache_policy(@policy, initial_entry: entry) @@ -103,7 +103,7 @@ defmodule ConfigCat.CachePolicy.LazyTest do assert :ok = CachePolicy.force_refresh(instance_id) - assert {:ok, ^settings, new_fetch_time_ms} = CachePolicy.get(instance_id) + assert {:ok, ^feature_flags, new_fetch_time_ms} = CachePolicy.get(instance_id) assert before <= new_fetch_time_ms && new_fetch_time_ms <= FetchTime.now_ms() end diff --git a/test/config_cat/cache_policy/manual_test.exs b/test/config_cat/cache_policy/manual_test.exs index 18332757..86cb578f 100644 --- a/test/config_cat/cache_policy/manual_test.exs +++ b/test/config_cat/cache_policy/manual_test.exs @@ -30,18 +30,18 @@ defmodule ConfigCat.CachePolicy.ManualTest do end describe "refreshing the config" do - test "stores new config in the cache", %{entry: entry, settings: settings} do + test "stores new config in the cache", %{entry: entry, feature_flags: feature_flags} do {:ok, instance_id} = start_cache_policy(@policy) expect_refresh(entry) assert :ok = CachePolicy.force_refresh(instance_id) - assert {:ok, settings, entry.fetch_time_ms} == CachePolicy.get(instance_id) + assert {:ok, feature_flags, entry.fetch_time_ms} == CachePolicy.get(instance_id) end test "updates fetch time when server responds that the config hasn't changed", %{ entry: entry, - settings: settings + feature_flags: feature_flags } do entry = Map.update!(entry, :fetch_time_ms, &(&1 - 200)) {:ok, instance_id} = start_cache_policy(@policy, initial_entry: entry) @@ -52,7 +52,7 @@ defmodule ConfigCat.CachePolicy.ManualTest do assert :ok = CachePolicy.force_refresh(instance_id) - assert {:ok, ^settings, new_fetch_time_ms} = CachePolicy.get(instance_id) + assert {:ok, ^feature_flags, new_fetch_time_ms} = CachePolicy.get(instance_id) assert before <= new_fetch_time_ms && new_fetch_time_ms <= FetchTime.now_ms() end diff --git a/test/config_cat/cache_test.exs b/test/config_cat/cache_test.exs index bf10da1e..7624e290 100644 --- a/test/config_cat/cache_test.exs +++ b/test/config_cat/cache_test.exs @@ -9,7 +9,7 @@ defmodule ConfigCat.CacheTest do alias ConfigCat.Hooks alias ConfigCat.MockConfigCache - @config Config.new_with_settings(%{}) + @config Config.new() @entry ConfigEntry.new(@config, "ETAG") @serialized ConfigEntry.serialize(@entry) diff --git a/test/config_cat/data_governance_test.exs b/test/config_cat/data_governance_test.exs index 03bd8c6d..8e7372d9 100644 --- a/test/config_cat/data_governance_test.exs +++ b/test/config_cat/data_governance_test.exs @@ -5,6 +5,7 @@ defmodule ConfigCat.ConfigFetcher.DataGovernanceTest do alias ConfigCat.CacheControlConfigFetcher, as: ConfigFetcher alias ConfigCat.Config + alias ConfigCat.Config.Preferences alias ConfigCat.ConfigEntry alias ConfigCat.Hooks alias ConfigCat.MockAPI @@ -272,8 +273,10 @@ defmodule ConfigCat.ConfigFetcher.DataGovernanceTest do end defp stub_response(response_uri, redirect_mode) do - response_uri - |> Config.new_with_preferences(redirect_mode) + preferences = Preferences.new(base_url: response_uri, redirect_mode: redirect_mode) + + [preferences: preferences] + |> Config.new() |> Jason.encode!() end diff --git a/test/config_cat/default_user_test.exs b/test/config_cat/default_user_test.exs index b851ef45..2450b27f 100644 --- a/test/config_cat/default_user_test.exs +++ b/test/config_cat/default_user_test.exs @@ -9,7 +9,7 @@ defmodule ConfigCat.DefaultUserTest do @moduletag capture_log: true setup do - settings = ~J""" + feature_flags = ~J""" { "testBoolKey": {"v": true,"t": 0, "p": [],"r": []}, "testStringKey": { @@ -21,7 +21,7 @@ defmodule ConfigCat.DefaultUserTest do } """ - stub_cached_settings({:ok, settings, FetchTime.now_ms()}) + stub_cached_feature_flags({:ok, feature_flags, FetchTime.now_ms()}) :ok end diff --git a/test/config_cat/hooks_test.exs b/test/config_cat/hooks_test.exs index 62a1f24c..dba44b4f 100644 --- a/test/config_cat/hooks_test.exs +++ b/test/config_cat/hooks_test.exs @@ -7,6 +7,7 @@ defmodule ConfigCat.HooksTest do alias ConfigCat.CachePolicy alias ConfigCat.Client alias ConfigCat.Config + alias ConfigCat.Config.RolloutRule alias ConfigCat.ConfigEntry alias ConfigCat.EvaluationDetails alias ConfigCat.Hooks @@ -14,8 +15,6 @@ defmodule ConfigCat.HooksTest do alias ConfigCat.NullDataSource alias ConfigCat.User - require ConfigCat.Constants, as: Constants - @moduletag capture_log: true @config ~J""" @@ -84,11 +83,11 @@ defmodule ConfigCat.HooksTest do value = ConfigCat.get_value("testStringKey", "", client: instance_id) - {:ok, settings} = Config.fetch_settings(@config) + feature_flags = Config.feature_flags(@config) assert value == "testValue" assert_received :on_client_ready - assert_received {:on_config_changed, ^settings} + assert_received {:on_config_changed, ^feature_flags} assert_received {:on_flag_evaluated, _details} refute_received {:on_error, _error} @@ -112,11 +111,11 @@ defmodule ConfigCat.HooksTest do value = ConfigCat.get_value("testStringKey", "", client: instance_id) - {:ok, settings} = Config.fetch_settings(@config) + feature_flags = Config.feature_flags(@config) assert value == "testValue" assert_received :on_client_ready - assert_received {:on_config_changed, ^settings} + assert_received {:on_config_changed, ^feature_flags} assert_received {:on_flag_evaluated, _details} refute_received {:on_error, _error} refute_received _any_other_messages @@ -140,16 +139,20 @@ defmodule ConfigCat.HooksTest do assert_received {:on_flag_evaluated, details} + rule = + RolloutRule.new( + comparator: 2, + comparison_attribute: "Identifier", + comparison_value: "@test1.com", + value: "fake1", + variation_id: "id1" + ) + assert %EvaluationDetails{ default_value?: false, error: nil, key: "testStringKey", - matched_evaluation_rule: %{ - Constants.comparator() => 2, - Constants.comparison_attribute() => "Identifier", - Constants.comparison_value() => "@test1.com", - Constants.value() => "fake1" - }, + matched_evaluation_rule: ^rule, matched_evaluation_percentage_rule: nil, user: ^user, value: "fake1", diff --git a/test/config_cat_test.exs b/test/config_cat_test.exs index ea802520..38079153 100644 --- a/test/config_cat_test.exs +++ b/test/config_cat_test.exs @@ -4,17 +4,16 @@ defmodule ConfigCatTest do import Jason.Sigil import Mox + alias ConfigCat.Config.RolloutRule alias ConfigCat.EvaluationDetails alias ConfigCat.FetchTime alias ConfigCat.User - require ConfigCat.Constants, as: Constants - setup :verify_on_exit! describe "when the configuration has been fetched" do setup do - settings = ~J""" + feature_flags = ~J""" { "testBoolKey": {"v": true,"t": 0, "p": [],"r": []}, "testStringKey": {"v": "testValue", "i": "id", "t": 1, "p": [],"r": [ @@ -31,7 +30,7 @@ defmodule ConfigCatTest do {:ok, client} = start_client() fetch_time_ms = FetchTime.now_ms() - stub_cached_settings({:ok, settings, fetch_time_ms}) + stub_cached_feature_flags({:ok, feature_flags, fetch_time_ms}) {:ok, client: client, fetch_time_ms: fetch_time_ms} end @@ -95,17 +94,21 @@ defmodule ConfigCatTest do {:ok, fetch_time} = FetchTime.to_datetime(fetch_time_ms) + rule = + RolloutRule.new( + comparator: 2, + comparison_attribute: "Identifier", + comparison_value: "@test1.com", + value: "fake1", + variation_id: "id1" + ) + assert %EvaluationDetails{ default_value?: false, error: nil, fetch_time: ^fetch_time, key: "testStringKey", - matched_evaluation_rule: %{ - Constants.comparator() => 2, - Constants.comparison_attribute() => "Identifier", - Constants.comparison_value() => "@test1.com", - Constants.value() => "fake1" - }, + matched_evaluation_rule: ^rule, matched_evaluation_percentage_rule: nil, user: ^user, value: "fake1", @@ -138,7 +141,7 @@ defmodule ConfigCatTest do setup do {:ok, client} = start_client() - stub_cached_settings({:error, :not_found}) + stub_cached_feature_flags({:error, :not_found}) {:ok, client: client} end diff --git a/test/flag_override_test.exs b/test/flag_override_test.exs index 463df69f..2ec8d7e1 100644 --- a/test/flag_override_test.exs +++ b/test/flag_override_test.exs @@ -10,13 +10,13 @@ defmodule ConfigCat.FlagOverrideTest do @moduletag capture_log: true setup do - settings = ~J""" + feature_flags = ~J""" { "fakeKey": {"v": false, "t": 0, "p": [],"r": []} } """ - stub_cached_settings({:ok, settings, FetchTime.now_ms()}) + stub_cached_feature_flags({:ok, feature_flags, FetchTime.now_ms()}) :ok end diff --git a/test/support/cache_policy_case.ex b/test/support/cache_policy_case.ex index ffe848e0..7e8c2e88 100644 --- a/test/support/cache_policy_case.ex +++ b/test/support/cache_policy_case.ex @@ -22,31 +22,31 @@ defmodule ConfigCat.CachePolicyCase do end setup do - settings = %{"some" => "settings"} + feature_flags = %{"some" => "feature_flags"} entry = - settings - |> Config.new_with_settings() + [feature_flags: feature_flags] + |> Config.new() |> ConfigEntry.new("ETag") - %{entry: entry, settings: settings} + %{entry: entry, feature_flags: feature_flags} end - @spec make_old_entry :: %{entry: ConfigEntry.t(), settings: Config.settings()} + @spec make_old_entry :: %{entry: ConfigEntry.t(), feature_flags: Config.feature_flags()} @spec make_old_entry(non_neg_integer()) :: %{ entry: ConfigEntry.t(), - settings: Config.settings() + feature_flags: Config.feature_flags() } def make_old_entry(age_ms \\ 0) do - settings = %{"old" => "settings"} + feature_flags = %{"old" => "feature_flags"} entry = - settings - |> Config.new_with_settings() + [feature_flags: feature_flags] + |> Config.new() |> ConfigEntry.new("OldETag") |> Map.update!(:fetch_time_ms, &(&1 - age_ms)) - %{entry: entry, settings: settings} + %{entry: entry, feature_flags: feature_flags} end @spec start_cache_policy(CachePolicy.t(), keyword()) :: {:ok, atom()} diff --git a/test/support/client_case.ex b/test/support/client_case.ex index b574f022..1c15f469 100644 --- a/test/support/client_case.ex +++ b/test/support/client_case.ex @@ -31,9 +31,12 @@ defmodule ConfigCat.ClientCase do {:ok, instance_id} end - @spec stub_cached_settings({:ok, Config.settings(), FetchTime.t()} | {:error, :not_found}) :: + @spec stub_cached_feature_flags( + {:ok, Config.feature_flags(), FetchTime.t()} + | {:error, :not_found} + ) :: :ok - def stub_cached_settings(response) do + def stub_cached_feature_flags(response) do Mox.stub(MockCachePolicy, :get, fn _id -> response end) :ok end