Current section

Files

Jump to
harness_ff_erlang_server_sdk src cfclient_instance.erl
Raw

src/cfclient_instance.erl

%% @doc
%% Feature flags client instance.
%%
%% It creates the ETS tables used to cache flag data from the server
%% and flag usage metrics. It runs periodic tasks to pull data from
%% the server and send metrics to it.
%%
%% An instance is started by the cfclient application.
%% More complex applications can start additional instances for a specific
%% project.
%%
%% @end
-module(cfclient_instance).
-behaviour(gen_server).
-include_lib("kernel/include/logger.hrl").
-include("cfclient_config.hrl").
-define(SERVER, ?MODULE).
% gen_server callbacks
-export([start_link/1, init/1, handle_call/3, handle_cast/2, handle_info/2]).
-spec start_link(proplists:proplist()) -> {ok, pid()} | ignore | {error, term()}.
start_link(Args) -> gen_server:start_link(?MODULE, Args, []).
init(Args) ->
ApiKey = proplists:get_value(api_key, Args),
Config0 = proplists:get_value(config, Args, []),
Config1 = cfclient_config:normalize(Config0),
ok = cfclient_config:create_tables(Config1),
ok = cfclient_config:set_config(Config1),
case cfclient_config:authenticate(ApiKey, Config1) of
{error, not_configured} ->
% Used during testing
{ok, Config1};
{error, Reason} ->
?LOG_ERROR("Authentication failed: ~p", [Reason]),
{stop, authenticate};
{ok, Config} ->
ok = cfclient_config:set_config(Config),
retrieve_flags(Config),
start_poll(Config),
start_analytics(Config),
{ok, Config}
end.
handle_info(metrics, Config) ->
?LOG_DEBUG("Metrics triggered"),
#{analytics_push_interval := AnalyticsPushInterval} = Config,
cfclient_metrics:process_metrics(Config),
erlang:send_after(AnalyticsPushInterval, self(), metrics),
{noreply, Config};
handle_info(poll, Config) ->
?LOG_DEBUG("Poll triggered"),
#{poll_interval := PollInterval} = Config,
retrieve_flags(Config),
erlang:send_after(PollInterval, self(), poll),
{noreply, Config}.
handle_call(_, _From, State) -> {reply, ok, State}.
handle_cast(_, State) -> {noreply, State}.
start_poll(#{poll_enabled := true} = Config) ->
#{analytics_push_interval := Interval} = Config,
erlang:send_after(Interval, self(), poll);
start_poll(_) -> ok.
start_analytics(#{analytics_enabled := true} = Config) ->
#{analytics_push_interval := Interval} = Config,
erlang:send_after(Interval, self(), metrics);
start_analytics(_) -> ok.
-spec retrieve_flags(cfclient:config()) -> ok.
retrieve_flags(#{poll_enabled := true} = Config) ->
case cfclient_retrieve:retrieve_flags(Config) of
{ok, Flags} -> [cfclient_cache:cache_flag(F, Config) || F <- Flags];
{error, Reason} -> ?LOG_WARNING("Could not retrive flags from API: ~p", [Reason])
end,
case cfclient_retrieve:retrieve_segments(Config) of
{ok, Segments} -> [cfclient_cache:cache_segment(S, Config) || S <- Segments];
{error, Reason1} -> ?LOG_WARNING("Could not retrive segments from API: ~p", [Reason1])
end,
ok;
retrieve_flags(_) -> ok.
% Ensure value is binary
% to_binary(Value) when is_binary(Value) -> Value;
% to_binary(Value) when is_atom(Value) -> atom_to_binary(Value);
% to_binary(Value) when is_list(Value) -> list_to_binary(Value).