Skip to content

Commit d40050f

Browse files
author
Bernd Warmuth
committed
feat: implemented support for tracking
Signed-off-by: Bernd Warmuth <[email protected]>
1 parent 234062c commit d40050f

9 files changed

+365
-25
lines changed

src/main/java/dev/openfeature/sdk/Client.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@
55
/**
66
* Interface used to resolve flags of varying types.
77
*/
8-
public interface Client extends Features, EventBus<Client> {
8+
public interface Client extends Features, Tracking, EventBus<Client> {
99
ClientMetadata getMetadata();
1010

1111
/**
1212
* Return an optional client-level evaluation context.
13+
*
1314
* @return {@link EvaluationContext}
1415
*/
1516
EvaluationContext getEvaluationContext();
1617

1718
/**
1819
* Set the client-level evaluation context.
20+
*
1921
* @param ctx Client level context.
2022
*/
2123
Client setEvaluationContext(EvaluationContext ctx);
@@ -30,12 +32,14 @@ public interface Client extends Features, EventBus<Client> {
3032

3133
/**
3234
* Fetch the hooks associated to this client.
35+
*
3336
* @return A list of {@link Hook}s.
3437
*/
3538
List<Hook> getHooks();
3639

3740
/**
3841
* Returns the current state of the associated provider.
42+
*
3943
* @return the provider state
4044
*/
4145
ProviderState getProviderState();

src/main/java/dev/openfeature/sdk/FeatureProvider.java

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.openfeature.sdk;
22

3+
import javax.annotation.Nullable;
34
import java.util.ArrayList;
45
import java.util.List;
56

@@ -71,4 +72,14 @@ default ProviderState getState() {
7172
return ProviderState.READY;
7273
}
7374

75+
/**
76+
* Feature provider implementations can opt in for to support Tracking by implementing this method.
77+
*
78+
* @param eventName The name of the tracking event
79+
* @param context Evaluation context used in flag evaluation (Optional)
80+
* @param details Data pertinent to a particular tracking event (Optional)
81+
*/
82+
default void track(String eventName, @Nullable EvaluationContext context, @Nullable TrackingEventDetails details) {
83+
84+
}
7485
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package dev.openfeature.sdk;
2+
3+
import dev.openfeature.sdk.internal.ExcludeFromGeneratedCoverageReport;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Getter;
6+
import lombok.ToString;
7+
import lombok.experimental.Delegate;
8+
9+
import java.util.Map;
10+
import java.util.function.Function;
11+
12+
/**
13+
* MutableTrackingEventDetails represents data pertinent to a particular tracking event.
14+
*/
15+
@EqualsAndHashCode
16+
@ToString
17+
public class MutableTrackingEventDetails implements TrackingEventDetails {
18+
19+
20+
@Getter
21+
private final float target;
22+
@Delegate(excludes = MutableTrackingEventDetails.DelegateExclusions.class)
23+
private final MutableStructure structure;
24+
25+
public MutableTrackingEventDetails() {
26+
this.target = 0f;
27+
this.structure = new MutableStructure();
28+
}
29+
30+
public MutableTrackingEventDetails(final float target) {
31+
this.target = target;
32+
this.structure = new MutableStructure();
33+
}
34+
35+
36+
@SuppressWarnings("all")
37+
private static class DelegateExclusions {
38+
@ExcludeFromGeneratedCoverageReport
39+
public <T extends Structure> Map<String, Value> merge(Function<Map<String, Value>, Structure> newStructure,
40+
Map<String, Value> base,
41+
Map<String, Value> overriding) {
42+
return null;
43+
}
44+
}
45+
}

src/main/java/dev/openfeature/sdk/OpenFeatureClient.java

+80-22
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
package dev.openfeature.sdk;
22

3-
import java.util.ArrayList;
4-
import java.util.Arrays;
5-
import java.util.Collections;
6-
import java.util.HashMap;
7-
import java.util.Map;
8-
import java.util.List;
9-
import java.util.function.Consumer;
10-
113
import dev.openfeature.sdk.exceptions.ExceptionUtils;
124
import dev.openfeature.sdk.exceptions.FatalError;
135
import dev.openfeature.sdk.exceptions.GeneralError;
@@ -19,6 +11,16 @@
1911
import lombok.Getter;
2012
import lombok.extern.slf4j.Slf4j;
2113

14+
import javax.annotation.Nullable;
15+
import java.util.ArrayList;
16+
import java.util.Arrays;
17+
import java.util.Collections;
18+
import java.util.HashMap;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.Objects;
22+
import java.util.function.Consumer;
23+
2224
/**
2325
* OpenFeature Client implementation.
2426
* You should not instantiate this or reference this class.
@@ -28,8 +30,8 @@
2830
* @deprecated // TODO: eventually we will make this non-public. See issue #872
2931
*/
3032
@Slf4j
31-
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.BeanMembersShouldSerialize", "PMD.UnusedLocalVariable",
32-
"unchecked", "rawtypes" })
33+
@SuppressWarnings({"PMD.DataflowAnomalyAnalysis", "PMD.BeanMembersShouldSerialize", "PMD.UnusedLocalVariable",
34+
"unchecked", "rawtypes"})
3335
@Deprecated() // TODO: eventually we will make this non-public. See issue #872
3436
public class OpenFeatureClient implements Client {
3537

@@ -67,11 +69,56 @@ public OpenFeatureClient(
6769
this.hookSupport = new HookSupport();
6870
}
6971

72+
/**
73+
* {@inheritDoc}
74+
*/
7075
@Override
7176
public ProviderState getProviderState() {
7277
return openfeatureApi.getFeatureProviderStateManager(domain).getState();
7378
}
7479

80+
/**
81+
* {@inheritDoc}
82+
*/
83+
@Override
84+
public void track(String trackingEventName) {
85+
Objects.requireNonNull(trackingEventName);
86+
invokeTrack(trackingEventName, null, null);
87+
}
88+
89+
90+
/**
91+
* {@inheritDoc}
92+
*/
93+
@Override
94+
public void track(String trackingEventName, EvaluationContext context) {
95+
Objects.requireNonNull(trackingEventName);
96+
Objects.requireNonNull(context);
97+
invokeTrack(trackingEventName, context, null);
98+
}
99+
100+
/**
101+
* {@inheritDoc}
102+
*/
103+
@Override
104+
public void track(String trackingEventName, TrackingEventDetails details) {
105+
Objects.requireNonNull(trackingEventName);
106+
Objects.requireNonNull(details);
107+
invokeTrack(trackingEventName, null, details);
108+
}
109+
110+
/**
111+
* {@inheritDoc}
112+
*/
113+
@Override
114+
public void track(String trackingEventName, EvaluationContext context, TrackingEventDetails details) {
115+
Objects.requireNonNull(trackingEventName);
116+
Objects.requireNonNull(context);
117+
Objects.requireNonNull(details);
118+
invokeTrack(trackingEventName, mergeEvaluationContext(context), details);
119+
}
120+
121+
75122
/**
76123
* {@inheritDoc}
77124
*/
@@ -115,7 +162,7 @@ public EvaluationContext getEvaluationContext() {
115162
}
116163

117164
private <T> FlagEvaluationDetails<T> evaluateFlag(FlagValueType type, String key, T defaultValue,
118-
EvaluationContext ctx, FlagEvaluationOptions options) {
165+
EvaluationContext ctx, FlagEvaluationOptions options) {
119166
FlagEvaluationOptions flagOptions = ObjectUtils.defaultIfNull(options,
120167
() -> FlagEvaluationOptions.builder().build());
121168
Map<String, Object> hints = Collections.unmodifiableMap(flagOptions.getHookHints());
@@ -183,6 +230,17 @@ private static <T> void enrichDetailsWithErrorDefaults(T defaultValue, FlagEvalu
183230
details.setReason(Reason.ERROR.toString());
184231
}
185232

233+
private void invokeTrack(String trackingEventName,
234+
@Nullable EvaluationContext context,
235+
@Nullable TrackingEventDetails details) {
236+
if ("".equals(trackingEventName)) {
237+
throw new IllegalArgumentException("trackingEventName cannot be empty");
238+
}
239+
openfeatureApi.getFeatureProviderStateManager(domain)
240+
.getProvider()
241+
.track(trackingEventName, mergeEvaluationContext(context), details);
242+
}
243+
186244
/**
187245
* Merge invocation contexts with API, transaction and client contexts.
188246
* Does not merge before context.
@@ -244,7 +302,7 @@ public Boolean getBooleanValue(String key, Boolean defaultValue, EvaluationConte
244302

245303
@Override
246304
public Boolean getBooleanValue(String key, Boolean defaultValue, EvaluationContext ctx,
247-
FlagEvaluationOptions options) {
305+
FlagEvaluationOptions options) {
248306
return getBooleanDetails(key, defaultValue, ctx, options).getValue();
249307
}
250308

@@ -260,7 +318,7 @@ public FlagEvaluationDetails<Boolean> getBooleanDetails(String key, Boolean defa
260318

261319
@Override
262320
public FlagEvaluationDetails<Boolean> getBooleanDetails(String key, Boolean defaultValue, EvaluationContext ctx,
263-
FlagEvaluationOptions options) {
321+
FlagEvaluationOptions options) {
264322
return this.evaluateFlag(FlagValueType.BOOLEAN, key, defaultValue, ctx, options);
265323
}
266324

@@ -276,7 +334,7 @@ public String getStringValue(String key, String defaultValue, EvaluationContext
276334

277335
@Override
278336
public String getStringValue(String key, String defaultValue, EvaluationContext ctx,
279-
FlagEvaluationOptions options) {
337+
FlagEvaluationOptions options) {
280338
return getStringDetails(key, defaultValue, ctx, options).getValue();
281339
}
282340

@@ -292,7 +350,7 @@ public FlagEvaluationDetails<String> getStringDetails(String key, String default
292350

293351
@Override
294352
public FlagEvaluationDetails<String> getStringDetails(String key, String defaultValue, EvaluationContext ctx,
295-
FlagEvaluationOptions options) {
353+
FlagEvaluationOptions options) {
296354
return this.evaluateFlag(FlagValueType.STRING, key, defaultValue, ctx, options);
297355
}
298356

@@ -308,7 +366,7 @@ public Integer getIntegerValue(String key, Integer defaultValue, EvaluationConte
308366

309367
@Override
310368
public Integer getIntegerValue(String key, Integer defaultValue, EvaluationContext ctx,
311-
FlagEvaluationOptions options) {
369+
FlagEvaluationOptions options) {
312370
return getIntegerDetails(key, defaultValue, ctx, options).getValue();
313371
}
314372

@@ -324,7 +382,7 @@ public FlagEvaluationDetails<Integer> getIntegerDetails(String key, Integer defa
324382

325383
@Override
326384
public FlagEvaluationDetails<Integer> getIntegerDetails(String key, Integer defaultValue, EvaluationContext ctx,
327-
FlagEvaluationOptions options) {
385+
FlagEvaluationOptions options) {
328386
return this.evaluateFlag(FlagValueType.INTEGER, key, defaultValue, ctx, options);
329387
}
330388

@@ -340,7 +398,7 @@ public Double getDoubleValue(String key, Double defaultValue, EvaluationContext
340398

341399
@Override
342400
public Double getDoubleValue(String key, Double defaultValue, EvaluationContext ctx,
343-
FlagEvaluationOptions options) {
401+
FlagEvaluationOptions options) {
344402
return this.evaluateFlag(FlagValueType.DOUBLE, key, defaultValue, ctx, options).getValue();
345403
}
346404

@@ -356,7 +414,7 @@ public FlagEvaluationDetails<Double> getDoubleDetails(String key, Double default
356414

357415
@Override
358416
public FlagEvaluationDetails<Double> getDoubleDetails(String key, Double defaultValue, EvaluationContext ctx,
359-
FlagEvaluationOptions options) {
417+
FlagEvaluationOptions options) {
360418
return this.evaluateFlag(FlagValueType.DOUBLE, key, defaultValue, ctx, options);
361419
}
362420

@@ -372,7 +430,7 @@ public Value getObjectValue(String key, Value defaultValue, EvaluationContext ct
372430

373431
@Override
374432
public Value getObjectValue(String key, Value defaultValue, EvaluationContext ctx,
375-
FlagEvaluationOptions options) {
433+
FlagEvaluationOptions options) {
376434
return getObjectDetails(key, defaultValue, ctx, options).getValue();
377435
}
378436

@@ -383,13 +441,13 @@ public FlagEvaluationDetails<Value> getObjectDetails(String key, Value defaultVa
383441

384442
@Override
385443
public FlagEvaluationDetails<Value> getObjectDetails(String key, Value defaultValue,
386-
EvaluationContext ctx) {
444+
EvaluationContext ctx) {
387445
return getObjectDetails(key, defaultValue, ctx, FlagEvaluationOptions.builder().build());
388446
}
389447

390448
@Override
391449
public FlagEvaluationDetails<Value> getObjectDetails(String key, Value defaultValue, EvaluationContext ctx,
392-
FlagEvaluationOptions options) {
450+
FlagEvaluationOptions options) {
393451
return this.evaluateFlag(FlagValueType.OBJECT, key, defaultValue, ctx, options);
394452
}
395453

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package dev.openfeature.sdk;
2+
3+
/**
4+
* Interface for Tracking events.
5+
*/
6+
public interface Tracking {
7+
/**
8+
* Performs tracking of a particular action or application state.
9+
*
10+
* @param trackingEventName Event name to track
11+
*/
12+
void track(String trackingEventName);
13+
14+
/**
15+
* Performs tracking of a particular action or application state.
16+
*
17+
* @param trackingEventName Event name to track
18+
* @param context Evaluation context used in flag evaluation
19+
*/
20+
void track(String trackingEventName, EvaluationContext context);
21+
22+
/**
23+
* Performs tracking of a particular action or application state.
24+
*
25+
* @param trackingEventName Event name to track
26+
* @param details Data pertinent to a particular tracking event
27+
*/
28+
void track(String trackingEventName, TrackingEventDetails details);
29+
30+
/**
31+
* Performs tracking of a particular action or application state.
32+
*
33+
* @param trackingEventName Event name to track
34+
* @param context Evaluation context used in flag evaluation
35+
* @param details Data pertinent to a particular tracking event
36+
*/
37+
void track(String trackingEventName, EvaluationContext context, TrackingEventDetails details);
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package dev.openfeature.sdk;
2+
3+
/**
4+
* Data pertinent to a particular tracking event.
5+
*/
6+
public interface TrackingEventDetails extends Structure {
7+
}

0 commit comments

Comments
 (0)