Skip to content

Commit

Permalink
feat: Add support for client-side prerequisite events. (#137)
Browse files Browse the repository at this point in the history
BEGIN_COMMIT_OVERRIDE
feat: Add support for client-side prerequisite events.
feat: Add support for client-side visibility for all_flags_state.
END_COMMIT_OVERRIDE

SDK-803
  • Loading branch information
kinyoklion authored Oct 24, 2024
1 parent 568ac51 commit 167308a
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 190 deletions.
16 changes: 16 additions & 0 deletions priv/flags-segments-put-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@
},
"prereqs-fail-off": {
"clientSide": true,
"clientSideAvailability": {
"usingEnvironmentId": true,
"usingMobileKey": false
},
"debugEventsUntilDate": null,
"deleted": false,
"fallthrough": {
Expand Down Expand Up @@ -124,6 +128,10 @@
},
"prereqs-fail-off-null": {
"clientSide": true,
"clientSideAvailability": {
"usingEnvironmentId": true,
"usingMobileKey": false
},
"debugEventsUntilDate": null,
"deleted": false,
"fallthrough": {
Expand Down Expand Up @@ -152,6 +160,10 @@
},
"prereqs-fail-variation": {
"clientSide": true,
"clientSideAvailability": {
"usingEnvironmentId": true,
"usingMobileKey": false
},
"debugEventsUntilDate": null,
"deleted": false,
"fallthrough": {
Expand Down Expand Up @@ -254,6 +266,10 @@
},
"prereqs-success": {
"clientSide": true,
"clientSideAvailability": {
"usingEnvironmentId": true,
"usingMobileKey": false
},
"debugEventsUntilDate": null,
"deleted": false,
"fallthrough": {
Expand Down
43 changes: 36 additions & 7 deletions src/ldclient_eval.erl
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ target_match
}.

-type all_flags_state_options() :: #{
with_reasons => boolean()
%% client_side_only => boolean(), % TODO: Support.
with_reasons => boolean(),
client_side_only => boolean()
%% details_only_for_tracked_flags => boolean() % TODO: Support.
}.

Expand Down Expand Up @@ -113,6 +113,26 @@ all_flags_state(Context, Options, Tag) ->
is_not_deleted(#{deleted := true}) -> false;
is_not_deleted(_) -> true.

-spec is_prereq_of(FlagKey :: ldclient_flag:key(), Event :: ldclient_event:event()) -> boolean().
is_prereq_of(FlagKey, #{data := #{prereq_of := PrereqOf}} = _Event) ->
FlagKey =:= PrereqOf.

-spec maybe_add_prerequisites(Prerequisites :: [ldclient_flag:key()], Map :: map()) -> map().
maybe_add_prerequisites([] = _Prerequisites, Map) -> Map;
maybe_add_prerequisites(Prerequisites, Map) -> Map#{<<"prerequisites">> => Prerequisites}.


-spec is_visible(Item :: map(), Options :: all_flags_state_options()) -> boolean().
is_visible(
#{clientSideAvailability := #{
usingEnvironmentId := UsingEnvironmentId
}} = _Item,
#{client_side_only := true} = _Options
) -> UsingEnvironmentId;
%% All flags will have the availability set via parsing. So when client_side_only is not true
%% we want everything to be visible.
is_visible(_, _) -> true.

-spec all_flags_state(
Context :: ldclient_context:context(),
Options :: all_flags_state_options(),
Expand All @@ -127,17 +147,26 @@ all_flags_state(_Context, _Options, _Tag, _, not_initialized) ->
all_flags_state(Context, #{with_reasons := WithReason} = _Options, Tag, Offline, store_initialized) ->
error_logger:warning_msg("Called allFlagsState before client initialization; using last known values from data store."),
all_flags_state(Context, #{with_reasons := WithReason} = _Options, Tag, Offline, initialized);
all_flags_state(Context, #{with_reasons := WithReason} = _Options, Tag, _, initialized) ->
all_flags_state(Context, #{with_reasons := WithReason} = Options, Tag, _, initialized) ->
FeatureStore = ldclient_config:get_value(Tag, feature_store),
AllFlags = [Flag || Flag = {_, FlagValue} <- FeatureStore:all(Tag, features), is_not_deleted(FlagValue)],
AllFlags = [Flag || Flag = {_, FlagValue} <- FeatureStore:all(Tag, features), is_not_deleted(FlagValue), is_visible(FlagValue, Options)],
EvalFun = fun({FlagKey, #{version := Version} = Flag}, #{<<"$flagsState">> := FlagsState} = Acc) ->
% Here the state is either initialized, or store_initialized, and we are online. Call directly to that version
% of flag_key_for_context. This will prevent additional warnings for the client initialization not being
% complete in the store_initialized state.
{{VariationIndex, V, Reason}, _Events} = flag_key_for_context(Tag, FlagKey, Context, null, online, initialized),
FlagState = maybe_add_track_events(Flag,
{{VariationIndex, V, Reason}, Events} = flag_key_for_context(Tag, FlagKey, Context, null, online, initialized),
DirectPrereqEvents = lists:filter(fun(Event) -> is_prereq_of(FlagKey, Event) end, Events),
Prereqs = lists:reverse(lists:map(fun(Event) ->
#{data := #{key := Key}} = Event,
Key
end,
DirectPrereqEvents
)),
FlagState =
maybe_add_prerequisites(Prereqs,
maybe_add_track_events(Flag,
maybe_add_debug_events_until_date(Flag, #{
<<"version">> => Version})),
<<"version">> => Version}))),
UpdatedFlagState = case is_integer(VariationIndex) of
true -> FlagState#{
<<"variation">> => VariationIndex
Expand Down
46 changes: 34 additions & 12 deletions src/ldclient_flag.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,22 @@

%% Types
-type flag() :: #{
debugEventsUntilDate => pos_integer() | null,
debugEventsUntilDate => pos_integer() | null,
deleted => boolean(),
fallthrough => variation_or_rollout(),
key => key(),
offVariation => variation(),
offVariation => variation(),
on => boolean(),
prerequisites => [prerequisite()],
rules => [ldclient_rule:rule()],
salt => binary(),
targets => [target()],
contextTargets => [target()],
trackEvents => boolean(),
trackEventsFallthrough => boolean(),
trackEvents => boolean(),
trackEventsFallthrough => boolean(),
variations => [variation_value()],
version => version()
version => version(),
clientSideAvailability => client_side_availability()
}.

-type key() :: binary().
Expand Down Expand Up @@ -62,6 +63,12 @@

-type version() :: non_neg_integer().

-type client_side_availability() :: #{
usingEnvironmentId => boolean(),
usingMobileKey => boolean()
}.
%% Describes the availability of the flag to client-side SDKs.

-export_type([flag/0]).
-export_type([key/0]).
-export_type([prerequisite/0]).
Expand Down Expand Up @@ -92,7 +99,11 @@ new(RawFlagMap) ->
<<"trackEvents">> => false,
<<"trackEventsFallthrough">> => false,
<<"variations">> => [],
<<"version">> => 0
<<"version">> => 0,
<<"clientSideAvailability">> => #{
<<"usingEnvironmentId">> => false,
<<"usingMobileKey">> => false
}
},
FlagMap = maps:merge(FlagTemplate, RawFlagMap),
new_from_template(FlagMap).
Expand Down Expand Up @@ -136,24 +147,35 @@ new_from_template(#{
<<"trackEvents">> := TrackEvents,
<<"trackEventsFallthrough">> := TrackEventsFallthrough,
<<"variations">> := Variations,
<<"version">> := Version
<<"version">> := Version,
<<"clientSideAvailability">> := ClientSideAvailability
}) ->
#{
debugEventsUntilDate => DebugEventsUntilDate,
debugEventsUntilDate => DebugEventsUntilDate,
deleted => Deleted,
fallthrough => parse_variation_or_rollout(Fallthrough),
key => Key,
offVariation => OffVariation,
offVariation => OffVariation,
on => On,
prerequisites => parse_prerequisites(Prerequisites),
rules => parse_rules(Rules),
salt => Salt,
targets => parse_targets(Targets),
contextTargets => parse_targets(ContextTargets),
trackEvents => TrackEvents,
trackEventsFallthrough => TrackEventsFallthrough,
trackEvents => TrackEvents,
trackEventsFallthrough => TrackEventsFallthrough,
variations => Variations,
version => Version
version => Version,
clientSideAvailability => parse_client_side_availability(ClientSideAvailability)
}.

-spec parse_client_side_availability(ClientSideAvailability :: map()) -> client_side_availability().
parse_client_side_availability(ClientSideAvailability) ->
UsingEnvironmentId = maps:get(<<"usingEnvironmentId">>, ClientSideAvailability, false),
UsingMobileKey = maps:get(<<"usingMobileKey">>, ClientSideAvailability, false),
#{
usingEnvironmentId => UsingEnvironmentId,
usingMobileKey => UsingMobileKey
}.

-spec parse_prerequisites([map()]) -> [prerequisite()].
Expand Down
4 changes: 3 additions & 1 deletion test-service/src/ts_service_request_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ get_service_detail(Req, State) ->
<<"anonymous-redaction">>,
<<"tls:custom-ca">>,
<<"tls:skip-verify-peer">>,
<<"tls:verify-peer">>
<<"tls:verify-peer">>,
<<"client-prereq-events">>,
<<"all-flags-client-side-only">>
],
<<"clientVersion">> => ldclient_config:get_version()
}),
Expand Down
Loading

0 comments on commit 167308a

Please sign in to comment.