%% @doc %% Funcctions to pull feature and target configuration from server via the API. %% @end -module(cfclient_retrieve). -export([retrieve_flags/1, retrieve_segments/1]). -type flag() :: cfapi_feature_config:cfapi_feature_config(). -type segment() :: cfapi_segment:cfapi_segment(). -type config() :: map(). -include_lib("kernel/include/logger.hrl"). % @doc Retrieve all features from Feature Flags API. -spec retrieve_flags(config()) -> {ok, [flag()]} | {error, Reason :: term()}. retrieve_flags(Config) -> #{auth_token := AuthToken, project := Project, config_url := ConfigUrl} = Config, #{environment := Env, clusterIdentifier := Cluster} = Project, Opts = #{ cfg => #{ auth => #{'BearerAuth' => <<"Bearer ", AuthToken/binary>>}, host => ConfigUrl, hackney_opts => [{timeout, 1}] }, params => #{cluster => Cluster} }, % Maximum number of retries RetryLimit = 5, % Initial delay between retries in milliseconds RetryDelay = 1000, retrieve_with_retry( fun cfapi_client_api:get_feature_config/3, [ctx:new(), Env, Opts], RetryLimit, RetryDelay, flags ). % @doc Retrieve all segments from Feature Flags API. -spec retrieve_segments(config()) -> {ok, [segment()]} | {error, Reason :: term()}. retrieve_segments(Config) -> #{auth_token := AuthToken, project := Project, config_url := ConfigUrl} = Config, #{environment := Env, clusterIdentifier := Cluster} = Project, RetryLimit = 5, RetryDelay = 1000, Opts = #{ cfg => #{ auth => #{'BearerAuth' => <<"Bearer ", AuthToken/binary>>}, host => ConfigUrl, hackney_opts => [{timeout, 20000}] }, params => #{cluster => Cluster} }, retrieve_with_retry( fun cfapi_client_api:get_all_segments/3, [ctx:new(), Env, Opts], RetryLimit, RetryDelay, segments ). % Recursive function for retrying the request retrieve_with_retry(Func, Args, RetryLimit, RetryDelay, Endpoint) -> case apply(Func, Args) of {ok, Values, _} -> {ok, Values}; % Retry on certain status codes {error, Reason, Response} -> case cfclient_config:is_retry_code(Response) of true when RetryLimit > 0 -> timer:sleep(RetryDelay), NewRetryLimit = RetryLimit - 1, NewRetryDelay = RetryDelay * 2, ?LOG_WARNING( "Error retrieving ~p: ~p, retrying with ~p: attempts left", [Endpoint, Reason, NewRetryLimit] ), retrieve_with_retry(Func, Args, NewRetryLimit, NewRetryDelay, Endpoint); _ -> {error, Reason} end; % Retry on request errors {error, Reason} when RetryLimit > 0 -> timer:sleep(RetryDelay), NewRetryLimit = RetryLimit - 1, % Exponential backoff NewRetryDelay = RetryDelay * 2, ?LOG_WARNING( "Error retrieving ~p: ~p, retrying with: ~p attempts left", [Endpoint, Reason, NewRetryLimit] ), retrieve_with_retry(Func, Args, NewRetryLimit, NewRetryDelay, Endpoint); {error, Reason} when RetryLimit == 0 -> {error, Reason} end.