Packages
launchdarkly_server_sdk
2.1.2
3.11.0
3.10.1
3.9.0
3.8.1
3.8.0
3.7.2
3.7.1
3.7.0
3.6.0
3.5.0
3.4.0
3.3.1
3.3.0
3.2.0
3.1.0
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.1.2
2.1.1
2.1.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.6.0
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.3
1.1.2
1.1.1
retired
1.1.0
retired
1.0.1
retired
1.0.0
retired
1.0.0-beta4
retired
1.0.0-beta3
retired
1.0.0-beta2
retired
LaunchDarkly SDK for Erlang
Current section
Files
Jump to
Current section
Files
src/ldclient.erl
%%-------------------------------------------------------------------
%% @doc `ldclient' module
%%
%% Acts as an interface to most common SDK functions: starting and stopping
%% client instances, and evaluating feature flags for contexts.
%%
%% Most use cases only need a single client instance for the lifetime of
%% their application. Consider using multiple instances only if you need to
%% simultaneously access more than one environment. Do not start an instance
%% every time you need to make a variation or other SDK call.
%% @end
%%-------------------------------------------------------------------
-module(ldclient).
%% API
-export([start_instance/1]).
-export([start_instance/2]).
-export([start_instance/3]).
-export([stop_all_instances/0]).
-export([stop_instance/0]).
-export([stop_instance/1]).
-export([is_offline/1]).
-export([initialized/1]).
-export([variation/3]).
-export([variation/4]).
-export([variation_detail/3]).
-export([variation_detail/4]).
-export([all_flags_state/1]).
-export([all_flags_state/2]).
-export([all_flags_state/3]).
-export([identify/1]).
-export([identify/2]).
-export([track/3]).
-export([track/4]).
-export([track_metric/4]).
-export([track_metric/5]).
%% Constants
-define(DEFAULT_INSTANCE_NAME, default).
%%===================================================================
%% API
%%===================================================================
%% @doc Start client with default options
%%
%% The SDK key is required to connect. Default streamer URL, storage backend
%% and instance name `default' will be used.
%% @end
-spec start_instance(SdkKey :: string()) -> ok | {error, atom(), term()}.
start_instance(SdkKey) ->
start_instance(SdkKey, ?DEFAULT_INSTANCE_NAME).
%% @doc Start client with custom name or options
%%
%% When `TagOrOptions' is an atom, the instance is started with that name. When
%% it's a map, it can be used to start with custom options.
%% @end
-spec start_instance(SdkKey :: string(), TagOrOptions :: atom() | map()) ->
ok | {error, atom(), term()}.
start_instance(SdkKey, Tag) when is_list(SdkKey), is_atom(Tag) ->
start_instance(SdkKey, Tag, #{});
start_instance(SdkKey, Options) when is_list(SdkKey), is_map(Options) ->
start_instance(SdkKey, ?DEFAULT_INSTANCE_NAME, Options).
%% @doc Start client with custom name and options
%%
%% Specify both custom client name and options when starting the client.
%% @end
-spec start_instance(SdkKey :: string(), Tag :: atom(), Options :: map()) ->
ok | {error, atom(), term()}.
start_instance(SdkKey, Tag, Options) when is_list(SdkKey), is_atom(Tag), is_map(Options) ->
ldclient_instance:start(Tag, SdkKey, Options).
%% @doc Stop all client instances
%%
%% @end
-spec stop_all_instances() -> ok.
stop_all_instances() ->
ldclient_instance:stop_all().
%% @doc Stop client instance
%%
%% Stops the default client instance.
%% @end
-spec stop_instance() -> ok | {error, not_found, term()}.
stop_instance() ->
stop_instance(?DEFAULT_INSTANCE_NAME).
%% @doc Stop client with the custom name
%%
%% This is useful if a client instance was started with a custom name.
%% @end
-spec stop_instance(Tag :: atom()) -> ok | {error, not_found, term()}.
stop_instance(Tag) when is_atom(Tag) ->
ldclient_instance:stop(Tag).
%% @doc Returns whether the LaunchDarkly client is in offline mode.
%%
%% In some situations, you might want to stop making remote calls to LaunchDarkly and
%% fall back to default values for your feature flags, this tells you whether only default
%% values will be returned.
%% @end
-spec is_offline(Tag :: atom()) -> boolean().
is_offline(Tag) ->
ldclient_config:get_value(Tag, offline).
%% @doc Returns whether the LaunchDarkly client has initialized.
%%
%% If this value is true, it means the client has succeeded at some point in connecting to LaunchDarkly and
%% has received feature flag data. It could still have encountered a connection problem after that point, so
%% this does not guarantee that the flags are up to date. Alternatively, it could also mean that the client
%% is in offline mode.
%%
%% If this value is false, it means the client has not yet connected to LaunchDarkly, or has permanently
%% failed. In this state, feature flag evaluations will always return default values.
%% @end
-spec initialized(Tag :: atom()) -> boolean().
initialized(Tag) ->
IsOffline = is_offline(Tag),
UpdateProcessorInitialized = ldclient_instance:update_processor_initialized(Tag),
IsOffline or UpdateProcessorInitialized.
%% @doc Evaluate given flag key for given context
%%
%% Evaluates the flag and returns the resulting variation value. The default
%% value will be returned in case of any errors.
%% @end
-spec variation(FlagKey :: binary(), Context :: ldclient_user:user() | ldclient_context:context(), DefaultValue :: ldclient_eval:result_value()) ->
ldclient_eval:result_value().
variation(FlagKey, Context, DefaultValue) when is_binary(FlagKey), is_map(Context) ->
variation(FlagKey, ensure_context(Context), DefaultValue, ?DEFAULT_INSTANCE_NAME).
%% @doc Evaluate given flag key for given context and given client instance
%%
%% Evaluates the flag and returns the resulting variation value. The default
%% value will be returned in case of any errors.
%% @end
-spec variation(FlagKey :: binary(), Context :: ldclient_user:user() | ldclient_context:context(), DefaultValue :: ldclient_eval:result_value(), Tag :: atom()) ->
ldclient_eval:result_value().
variation(FlagKey, Context, DefaultValue, Tag) when is_binary(FlagKey), is_map(Context) ->
% Get evaluation result detail
{{_Index, Value, _Reason}, Events} = ldclient_eval:flag_key_for_context(Tag, FlagKey, ensure_context(Context), DefaultValue),
% Send events
SendEventsFun = fun(Event) -> ldclient_event_server:add_event(Tag, Event, #{}) end,
lists:foreach(SendEventsFun, Events),
% Return evaluation result
Value.
%% @doc Evaluate given flag key for given context
%%
%% Evaluates the flag and returns the result detail containing the variation
%% index, value, and reason why the specific result was chosen. The default
%% value will be returned in case of any errors.
%% @end
-spec variation_detail(FlagKey :: binary(), Context :: ldclient_user:user() | ldclient_context:context(), DefaultValue :: ldclient_eval:result_value()) ->
ldclient_eval:detail().
variation_detail(FlagKey, Context, DefaultValue) when is_binary(FlagKey), is_map(Context) ->
variation_detail(FlagKey, ensure_context(Context), DefaultValue, ?DEFAULT_INSTANCE_NAME).
%% @doc Evaluate given flag key for given context and given client instance
%%
%% Evaluates the flag and returns the result detail containing the variation
%% index, value, and reason why the specific result was chosen. The default
%% value will be returned in case of any errors.
%% @end
-spec variation_detail(FlagKey :: binary(), Context :: ldclient_user:user() | ldclient_context:context(), DefaultValue :: ldclient_eval:result_value(), Tag :: atom()) ->
ldclient_eval:detail().
variation_detail(FlagKey, Context, DefaultValue, Tag) when is_binary(FlagKey), is_map(Context) ->
% Get evaluation result detail
{Detail, Events} = ldclient_eval:flag_key_for_context(Tag, FlagKey, ensure_context(Context), DefaultValue),
% Send events
SendEventsFun = fun(Event) -> ldclient_event_server:add_event(Tag, Event, #{include_reasons => true}) end,
lists:foreach(SendEventsFun, Events),
% Return evaluation detail
Detail.
%% @doc Evaluate all flags for a given context and return their values
%%
%% Evaluates all existing flags, but does not create any events as a side
%% effect of the evaluation. It returns a map of flag keys to evaluated values.
%% @end
-spec all_flags_state(Context :: ldclient_user:user() | ldclient_context:context()) -> ldclient_eval:feature_flags_state().
all_flags_state(Context) ->
all_flags_state(Context, ?DEFAULT_INSTANCE_NAME).
%% @doc Evaluate all flags for a given context and given client instance
%%
%% Evaluates all existing flags, but does not create any events as a side
%% effect of the evaluation. It returns a map of flag keys to evaluated values.
%% @end
-spec all_flags_state(Context :: ldclient_user:user() | ldclient_context:context(), Tag :: atom()) -> ldclient_eval:feature_flags_state().
all_flags_state(Context, Tag) ->
ldclient_eval:all_flags_eval(ensure_context(Context), Tag).
%% @doc Returns an object that encapsulates the state of all feature flags for a given context.
%%
%% This includes the flag values, and also metadata that can be used on the front end.
%% The most common use case for this method is to bootstrap a set of client-side feature flags from a
%% back-end service.
%%
%% If you are not using this to boostrap a client, then you likely want all_flags_state/1 or all_flags_state/2.
%% @end
-spec all_flags_state(Context :: ldclient_user:user() | ldclient_context:context(),
Options :: ldclient_eval:all_flags_state_options(), Tag :: atom()) -> ldclient_eval:feature_flags_state().
all_flags_state(Context, Options, Tag) ->
ldclient_eval:all_flags_state(ensure_context(Context), Options, Tag).
%% @doc Identify reports details about a context
%%
%% This function uses the default client instance.
%% @end
-spec identify(Context :: ldclient_user:user() | ldclient_context:context()) -> ok.
identify(Context) ->
identify(ensure_context(Context), ?DEFAULT_INSTANCE_NAME).
%% @doc Identify reports details about a context
%%
%% This is useful to report context to a specific client instance.
%% @end
-spec identify(Context :: ldclient_user:user() | ldclient_context:context(), Tag :: atom()) -> ok.
identify(Context, Tag) when is_atom(Tag) ->
ConvertedContext = ensure_context(Context),
when_is_valid_context(ConvertedContext, false, fun() ->
Event = ldclient_event:new_identify(ConvertedContext),
ldclient_event_server:add_event(Tag, Event, #{})
end).
%% @doc Track reports that a context has performed an event
%%
%% Custom data can be attached to the event.
%% @end
-spec track(Key :: binary(), Context :: ldclient_user:user() | ldclient_context:context(), Data :: ldclient_event:event_data()) -> ok.
track(Key, Context, Data) when is_binary(Key), is_map(Data) ->
track(Key, ensure_context(Context), Data, ?DEFAULT_INSTANCE_NAME).
%% @doc Track reports that a context has performed an event
%%
%% This is useful for specifying a specific client instance.
%% @end
-spec track(Key :: binary(), Context :: ldclient_user:user() | ldclient_context:context(), Data :: ldclient_event:event_data(), Tag :: atom()) -> ok.
track(Key, Context, Data, Tag) when is_atom(Tag), is_binary(Key) ->
ConvertedContext = ensure_context(Context),
when_is_valid_context(ConvertedContext, false, fun() ->
Event = ldclient_event:new_custom(Key, ensure_context(Context), Data),
ldclient_event_server:add_event(Tag, Event, #{})
end).
%% @doc Reports that a context has performed an event, and associates it with a numeric value.
%%
%% This value is used by the LaunchDarkly experimentation feature in numeric custom metrics, and will also
%% be returned as part of the custom event for Data Export.
%%
%% Custom data can also be attached to the event.
%% @end
-spec track_metric(Key :: binary(), Context :: ldclient_user:user() | ldclient_context:context(), ldclient_event:event_data(), Metric :: number()) -> ok.
track_metric(Key, Context, Data, Metric) ->
track_metric(Key, ensure_context(Context), Data, Metric, ?DEFAULT_INSTANCE_NAME).
%% @doc Reports that a context has performed an event, and associates it with a numeric value.
%%
%% This value is used by the LaunchDarkly experimentation feature in numeric custom metrics, and will also
%% be returned as part of the custom event for Data Export.
%%
%% Custom data can also be attached to the event.
%% @end
-spec track_metric(Key :: binary(), Context :: ldclient_user:user() | ldclient_context:context(), ldclient_event:event_data(), Metric :: number(), Tag :: atom()) -> ok.
track_metric(Key, Context, Data, Metric, Tag) ->
ConvertedContext = ensure_context(Context),
when_is_valid_context(ConvertedContext, false, fun() ->
Event = ldclient_event:new_custom(Key, ensure_context(Context), Data, Metric),
ldclient_event_server:add_event(Tag, Event, #{})
end).
%% @doc Take a context or a context and return a context.
%%
%% If the context cannot be converted, then
%% @end
-spec ensure_context(UserOrContext :: ldclient_user:user() | ldclient_context:context()) -> ldclient_context:context().
ensure_context(#{kind := _Kind} = ContextOrContext) -> ContextOrContext;
ensure_context(UserOrContext) ->
case maps:is_key(key, UserOrContext) of
%% We allow legacy users to have an empty key, but we log it.
false -> error_logger:warning_msg("Context key is blank. Flag evaluation will proceed, but the context will not be stored in Launchdarkly");
true -> ok
end,
ldclient_context:new_from_user(UserOrContext).
%% @doc High order function that calls a function when a context is valid, otherwise returns true.
%%
%% @end
-spec when_is_valid_context(Context :: ldclient_context:context(), AllowEmptyKey :: boolean(), Fun :: function()) -> ok.
when_is_valid_context(Context, AllowEmptyKey, Fun) ->
case ldclient_context:is_valid(Context, AllowEmptyKey) of
true -> Fun();
false -> ok
end.