Skip to content

Commit 167308a

Browse files
authored
feat: Add support for client-side prerequisite events. (#137)
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
1 parent 568ac51 commit 167308a

File tree

6 files changed

+327
-190
lines changed

6 files changed

+327
-190
lines changed

priv/flags-segments-put-data.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@
9696
},
9797
"prereqs-fail-off": {
9898
"clientSide": true,
99+
"clientSideAvailability": {
100+
"usingEnvironmentId": true,
101+
"usingMobileKey": false
102+
},
99103
"debugEventsUntilDate": null,
100104
"deleted": false,
101105
"fallthrough": {
@@ -124,6 +128,10 @@
124128
},
125129
"prereqs-fail-off-null": {
126130
"clientSide": true,
131+
"clientSideAvailability": {
132+
"usingEnvironmentId": true,
133+
"usingMobileKey": false
134+
},
127135
"debugEventsUntilDate": null,
128136
"deleted": false,
129137
"fallthrough": {
@@ -152,6 +160,10 @@
152160
},
153161
"prereqs-fail-variation": {
154162
"clientSide": true,
163+
"clientSideAvailability": {
164+
"usingEnvironmentId": true,
165+
"usingMobileKey": false
166+
},
155167
"debugEventsUntilDate": null,
156168
"deleted": false,
157169
"fallthrough": {
@@ -254,6 +266,10 @@
254266
},
255267
"prereqs-success": {
256268
"clientSide": true,
269+
"clientSideAvailability": {
270+
"usingEnvironmentId": true,
271+
"usingMobileKey": false
272+
},
257273
"debugEventsUntilDate": null,
258274
"deleted": false,
259275
"fallthrough": {

src/ldclient_eval.erl

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ target_match
4040
}.
4141

4242
-type all_flags_state_options() :: #{
43-
with_reasons => boolean()
44-
%% client_side_only => boolean(), % TODO: Support.
43+
with_reasons => boolean(),
44+
client_side_only => boolean()
4545
%% details_only_for_tracked_flags => boolean() % TODO: Support.
4646
}.
4747

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

116+
-spec is_prereq_of(FlagKey :: ldclient_flag:key(), Event :: ldclient_event:event()) -> boolean().
117+
is_prereq_of(FlagKey, #{data := #{prereq_of := PrereqOf}} = _Event) ->
118+
FlagKey =:= PrereqOf.
119+
120+
-spec maybe_add_prerequisites(Prerequisites :: [ldclient_flag:key()], Map :: map()) -> map().
121+
maybe_add_prerequisites([] = _Prerequisites, Map) -> Map;
122+
maybe_add_prerequisites(Prerequisites, Map) -> Map#{<<"prerequisites">> => Prerequisites}.
123+
124+
125+
-spec is_visible(Item :: map(), Options :: all_flags_state_options()) -> boolean().
126+
is_visible(
127+
#{clientSideAvailability := #{
128+
usingEnvironmentId := UsingEnvironmentId
129+
}} = _Item,
130+
#{client_side_only := true} = _Options
131+
) -> UsingEnvironmentId;
132+
%% All flags will have the availability set via parsing. So when client_side_only is not true
133+
%% we want everything to be visible.
134+
is_visible(_, _) -> true.
135+
116136
-spec all_flags_state(
117137
Context :: ldclient_context:context(),
118138
Options :: all_flags_state_options(),
@@ -127,17 +147,26 @@ all_flags_state(_Context, _Options, _Tag, _, not_initialized) ->
127147
all_flags_state(Context, #{with_reasons := WithReason} = _Options, Tag, Offline, store_initialized) ->
128148
error_logger:warning_msg("Called allFlagsState before client initialization; using last known values from data store."),
129149
all_flags_state(Context, #{with_reasons := WithReason} = _Options, Tag, Offline, initialized);
130-
all_flags_state(Context, #{with_reasons := WithReason} = _Options, Tag, _, initialized) ->
150+
all_flags_state(Context, #{with_reasons := WithReason} = Options, Tag, _, initialized) ->
131151
FeatureStore = ldclient_config:get_value(Tag, feature_store),
132-
AllFlags = [Flag || Flag = {_, FlagValue} <- FeatureStore:all(Tag, features), is_not_deleted(FlagValue)],
152+
AllFlags = [Flag || Flag = {_, FlagValue} <- FeatureStore:all(Tag, features), is_not_deleted(FlagValue), is_visible(FlagValue, Options)],
133153
EvalFun = fun({FlagKey, #{version := Version} = Flag}, #{<<"$flagsState">> := FlagsState} = Acc) ->
134154
% Here the state is either initialized, or store_initialized, and we are online. Call directly to that version
135155
% of flag_key_for_context. This will prevent additional warnings for the client initialization not being
136156
% complete in the store_initialized state.
137-
{{VariationIndex, V, Reason}, _Events} = flag_key_for_context(Tag, FlagKey, Context, null, online, initialized),
138-
FlagState = maybe_add_track_events(Flag,
157+
{{VariationIndex, V, Reason}, Events} = flag_key_for_context(Tag, FlagKey, Context, null, online, initialized),
158+
DirectPrereqEvents = lists:filter(fun(Event) -> is_prereq_of(FlagKey, Event) end, Events),
159+
Prereqs = lists:reverse(lists:map(fun(Event) ->
160+
#{data := #{key := Key}} = Event,
161+
Key
162+
end,
163+
DirectPrereqEvents
164+
)),
165+
FlagState =
166+
maybe_add_prerequisites(Prereqs,
167+
maybe_add_track_events(Flag,
139168
maybe_add_debug_events_until_date(Flag, #{
140-
<<"version">> => Version})),
169+
<<"version">> => Version}))),
141170
UpdatedFlagState = case is_integer(VariationIndex) of
142171
true -> FlagState#{
143172
<<"variation">> => VariationIndex

src/ldclient_flag.erl

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,22 @@
1313

1414
%% Types
1515
-type flag() :: #{
16-
debugEventsUntilDate => pos_integer() | null,
16+
debugEventsUntilDate => pos_integer() | null,
1717
deleted => boolean(),
1818
fallthrough => variation_or_rollout(),
1919
key => key(),
20-
offVariation => variation(),
20+
offVariation => variation(),
2121
on => boolean(),
2222
prerequisites => [prerequisite()],
2323
rules => [ldclient_rule:rule()],
2424
salt => binary(),
2525
targets => [target()],
2626
contextTargets => [target()],
27-
trackEvents => boolean(),
28-
trackEventsFallthrough => boolean(),
27+
trackEvents => boolean(),
28+
trackEventsFallthrough => boolean(),
2929
variations => [variation_value()],
30-
version => version()
30+
version => version(),
31+
clientSideAvailability => client_side_availability()
3132
}.
3233

3334
-type key() :: binary().
@@ -62,6 +63,12 @@
6263

6364
-type version() :: non_neg_integer().
6465

66+
-type client_side_availability() :: #{
67+
usingEnvironmentId => boolean(),
68+
usingMobileKey => boolean()
69+
}.
70+
%% Describes the availability of the flag to client-side SDKs.
71+
6572
-export_type([flag/0]).
6673
-export_type([key/0]).
6774
-export_type([prerequisite/0]).
@@ -92,7 +99,11 @@ new(RawFlagMap) ->
9299
<<"trackEvents">> => false,
93100
<<"trackEventsFallthrough">> => false,
94101
<<"variations">> => [],
95-
<<"version">> => 0
102+
<<"version">> => 0,
103+
<<"clientSideAvailability">> => #{
104+
<<"usingEnvironmentId">> => false,
105+
<<"usingMobileKey">> => false
106+
}
96107
},
97108
FlagMap = maps:merge(FlagTemplate, RawFlagMap),
98109
new_from_template(FlagMap).
@@ -136,24 +147,35 @@ new_from_template(#{
136147
<<"trackEvents">> := TrackEvents,
137148
<<"trackEventsFallthrough">> := TrackEventsFallthrough,
138149
<<"variations">> := Variations,
139-
<<"version">> := Version
150+
<<"version">> := Version,
151+
<<"clientSideAvailability">> := ClientSideAvailability
140152
}) ->
141153
#{
142-
debugEventsUntilDate => DebugEventsUntilDate,
154+
debugEventsUntilDate => DebugEventsUntilDate,
143155
deleted => Deleted,
144156
fallthrough => parse_variation_or_rollout(Fallthrough),
145157
key => Key,
146-
offVariation => OffVariation,
158+
offVariation => OffVariation,
147159
on => On,
148160
prerequisites => parse_prerequisites(Prerequisites),
149161
rules => parse_rules(Rules),
150162
salt => Salt,
151163
targets => parse_targets(Targets),
152164
contextTargets => parse_targets(ContextTargets),
153-
trackEvents => TrackEvents,
154-
trackEventsFallthrough => TrackEventsFallthrough,
165+
trackEvents => TrackEvents,
166+
trackEventsFallthrough => TrackEventsFallthrough,
155167
variations => Variations,
156-
version => Version
168+
version => Version,
169+
clientSideAvailability => parse_client_side_availability(ClientSideAvailability)
170+
}.
171+
172+
-spec parse_client_side_availability(ClientSideAvailability :: map()) -> client_side_availability().
173+
parse_client_side_availability(ClientSideAvailability) ->
174+
UsingEnvironmentId = maps:get(<<"usingEnvironmentId">>, ClientSideAvailability, false),
175+
UsingMobileKey = maps:get(<<"usingMobileKey">>, ClientSideAvailability, false),
176+
#{
177+
usingEnvironmentId => UsingEnvironmentId,
178+
usingMobileKey => UsingMobileKey
157179
}.
158180

159181
-spec parse_prerequisites([map()]) -> [prerequisite()].

test-service/src/ts_service_request_handler.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ get_service_detail(Req, State) ->
7878
<<"anonymous-redaction">>,
7979
<<"tls:custom-ca">>,
8080
<<"tls:skip-verify-peer">>,
81-
<<"tls:verify-peer">>
81+
<<"tls:verify-peer">>,
82+
<<"client-prereq-events">>,
83+
<<"all-flags-client-side-only">>
8284
],
8385
<<"clientVersion">> => ldclient_config:get_version()
8486
}),

0 commit comments

Comments
 (0)