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_targets.erl
%%-------------------------------------------------------------------
%% @doc Methods for evaluating targets.
%% @private
%% @end
%%-------------------------------------------------------------------
-module(ldclient_targets).
%% API
-export([eval_flag_targets/2]).
-type match_result() :: no_match | {match, non_neg_integer()}.
%% @doc Evaluate the targets of the flag against the specified context.
%%
%% If there are no matches, then return `no_match'.
%%
%% If there is a match, then return `{match, Variation}'.
%% @end
-spec eval_flag_targets(Flag :: ldclient_flag:flag(), Context :: ldclient_context:context()) -> match_result().
eval_flag_targets(
#{targets := [], contextTargets := []} = _Flag,
_Context
) ->
%% There are no targets to evaluate, so there is no match.
no_match;
eval_flag_targets(
#{targets := UserTargets, contextTargets := []} = _Flag,
Context
) ->
%% The context targets are empty, so evaluate user targets only.
check_user_targets(UserTargets, Context);
eval_flag_targets(
#{targets := UserTargets, contextTargets := ContextTargets} = _Flag,
Context
) ->
check_context_targets(ContextTargets, UserTargets, Context).
%%===================================================================
%% Internal functions
%%===================================================================
%% @doc Check context targets for any matches.
%%
%% This logic is independent of user targets checking because context targets have special logic
%% for when their kind is "user". The values list will be empty, and instead the user targets should
%% be checked for a target of the same variation. This allows for user targets to remain compatible
%% with existing SDKs and also not need to be duplicated for context targets.
%% @end
-spec check_context_targets(
ContextTargets :: [ldclient_flag:target()],
UserTargets :: [ldclient_flag:target()],
Context :: ldclient_context:context()
) -> match_result().
check_context_targets([], _UserTargets, _Context) -> no_match;
check_context_targets([#{contextKind := ContextKind, variation := Variation} = ContextTarget|Rest] = _Target, UserTargets, Context) ->
Result = case ContextKind of
%% In the case the context targets are for a user kind, then we are just using it for ordering, and we want
%% To check a user target with the same variation as the context target.
<<"user">> -> eval_target(find_user_target_for_variation(Variation, UserTargets), Context);
%% This was not a user kind, so just evaluate the target.
_ -> eval_target(ContextTarget, Context)
end,
%% If there is no match, then continue checking, if there is a match, then stop early and return it.
case Result of
no_match ->check_context_targets(Rest, UserTargets, Context);
_ -> Result
end.
%% @doc Check the user targets of the flag to see if there are any matches.
%%
%% @end
-spec check_user_targets(UserTargets :: [ldclient_flag:target()], Context :: ldclient_context:context()) -> match_result().
check_user_targets([], _Context) ->
no_match;
check_user_targets([Target|Rest] = _Targets, Context) ->
case eval_target(Target, Context) of
%% There was not a match, so continue checking.
no_match -> check_user_targets(Rest, Context);
%% There was a match, so return it and stop checking.
Match -> Match
end.
%% @doc Find a user target associated with a given variation.
%%
%% If there is no user target with a matching variation, then null will be returned.
%%
%% If there is a user target with a matching variation, then that target will be returned.
%% @end
-spec find_user_target_for_variation(
Variation :: non_neg_integer(),
UserTargets :: [ldclient_flag:target()]
) -> ldclient_flag:target() | null.
find_user_target_for_variation(Variation, UserTargets) ->
find_user_target_for_variation_result(lists:search(fun(Target) ->
#{variation := TargetVariation} = Target,
TargetVariation =:= Variation
end, UserTargets)).
%% @doc Convenience method to convert a lists:search result into either null or a target().
%%
%% @end
-spec find_user_target_for_variation_result(Result :: false | {value, ldclient_flag:target()}) -> null | ldclient_flag:target().
find_user_target_for_variation_result(false) -> null;
find_user_target_for_variation_result({value, Value}) -> Value.
%% @doc Evaluate a single target against a context.
%%
%% @end
-spec eval_target(Target :: null | ldclient_flag:target(), Context :: ldclient_context:context()) -> match_result().
eval_target(null, _Context) -> no_match;
eval_target(
#{contextKind := ContextKind, values := Values, variation := Variation} = _Target,
Context
) ->
Key = ldclient_context:get_key(ContextKind, Context),
Match = eval_target_key(Key, Values),
eval_target_result(Match, Variation).
%% @doc Convenience method to search for a key in a list of values. If the key is null, then it returns
%% false instead of performing the search.
%% @end
-spec eval_target_key(Key :: null | binary(), Values :: [binary()]) -> boolean().
eval_target_key(null = _Key, _Values) -> false;
eval_target_key(Key, Values) -> lists:member(Key, Values).
%% @doc Convenience method for turning a search result, and a variation into a match_result().
%%
%% @end
-spec eval_target_result(Match :: boolean(), Variation :: pos_integer()) -> match_result().
eval_target_result(false = _Match, _Variation) -> no_match;
eval_target_result(true = _Match, Variation) -> {match, Variation}.