Skip to content

Commit 1fc9758

Browse files
mnoman09aliabbasrizvi
authored andcommitted
(feat): Feature Management getFeatureVariable Listener (#270)
1 parent 6198fd6 commit 1fc9758

File tree

3 files changed

+553
-69
lines changed

3 files changed

+553
-69
lines changed

core-api/src/main/java/com/optimizely/ab/Optimizely.java

Lines changed: 86 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -465,17 +465,13 @@ public Boolean getFeatureVariableBoolean(@Nonnull String featureKey,
465465
return null;
466466
}
467467

468-
String variableValue = getFeatureVariableValueForType(
469-
featureKey,
470-
variableKey,
471-
userId,
472-
attributes,
473-
FeatureVariable.VariableType.BOOLEAN
474-
);
475-
if (variableValue != null) {
476-
return Boolean.parseBoolean(variableValue);
477-
}
478-
return null;
468+
return getFeatureVariableValueForType(
469+
featureKey,
470+
variableKey,
471+
userId,
472+
attributes,
473+
FeatureVariable.VariableType.BOOLEAN
474+
);
479475
}
480476

481477
/**
@@ -514,20 +510,19 @@ public Double getFeatureVariableDouble(@Nonnull String featureKey,
514510
return null;
515511
}
516512

517-
String variableValue = getFeatureVariableValueForType(
518-
featureKey,
519-
variableKey,
520-
userId,
521-
attributes,
522-
FeatureVariable.VariableType.DOUBLE
523-
);
524-
if (variableValue != null) {
525-
try {
526-
return Double.parseDouble(variableValue);
527-
} catch (NumberFormatException exception) {
528-
logger.error("NumberFormatException while trying to parse \"" + variableValue +
529-
"\" as Double. " + exception);
530-
}
513+
Double variableValue = null;
514+
try {
515+
variableValue = getFeatureVariableValueForType(
516+
featureKey,
517+
variableKey,
518+
userId,
519+
attributes,
520+
FeatureVariable.VariableType.DOUBLE
521+
);
522+
return variableValue;
523+
} catch (Exception exception) {
524+
logger.error("NumberFormatException while trying to parse \"" + variableValue +
525+
"\" as Double. " + exception);
531526
}
532527
return null;
533528
}
@@ -567,21 +562,19 @@ public Integer getFeatureVariableInteger(@Nonnull String featureKey,
567562
logger.error("Optimizely instance is not valid, failing getFeatureVariableInteger call.");
568563
return null;
569564
}
570-
571-
String variableValue = getFeatureVariableValueForType(
572-
featureKey,
573-
variableKey,
574-
userId,
575-
attributes,
576-
FeatureVariable.VariableType.INTEGER
577-
);
578-
if (variableValue != null) {
579-
try {
580-
return Integer.parseInt(variableValue);
581-
} catch (NumberFormatException exception) {
582-
logger.error("NumberFormatException while trying to parse \"" + variableValue +
583-
"\" as Integer. " + exception.toString());
565+
try {
566+
Integer variableValue = getFeatureVariableValueForType(
567+
featureKey,
568+
variableKey,
569+
userId,
570+
attributes,
571+
FeatureVariable.VariableType.INTEGER
572+
);
573+
if (variableValue != null) {
574+
return variableValue;
584575
}
576+
} catch (Exception exception) {
577+
logger.error("NumberFormatException while trying to parse value as Integer. " + exception.toString());
585578
}
586579
return null;
587580
}
@@ -631,11 +624,11 @@ public String getFeatureVariableString(@Nonnull String featureKey,
631624
}
632625

633626
@VisibleForTesting
634-
String getFeatureVariableValueForType(@Nonnull String featureKey,
635-
@Nonnull String variableKey,
636-
@Nonnull String userId,
637-
@Nonnull Map<String, ?> attributes,
638-
@Nonnull FeatureVariable.VariableType variableType) {
627+
<T extends Object> T getFeatureVariableValueForType(@Nonnull String featureKey,
628+
@Nonnull String variableKey,
629+
@Nonnull String userId,
630+
@Nonnull Map<String, ?> attributes,
631+
@Nonnull FeatureVariable.VariableType variableType) {
639632
if (featureKey == null) {
640633
logger.warn("The featureKey parameter must be nonnull.");
641634
return null;
@@ -668,6 +661,7 @@ String getFeatureVariableValueForType(@Nonnull String featureKey,
668661
String variableValue = variable.getDefaultValue();
669662
Map<String, ?> copiedAttributes = copyAttributes(attributes);
670663
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, userId, copiedAttributes);
664+
Boolean featureEnabled = false;
671665
if (featureDecision.variation != null) {
672666
if (featureDecision.variation.getFeatureEnabled()) {
673667
FeatureVariableUsageInstance featureVariableUsageInstance =
@@ -683,14 +677,61 @@ String getFeatureVariableValueForType(@Nonnull String featureKey,
683677
featureKey, featureDecision.variation.getKey(), variableValue, variableKey
684678
);
685679
}
680+
featureEnabled = featureDecision.variation.getFeatureEnabled();
686681
} else {
687682
logger.info("User \"{}\" was not bucketed into any variation for feature flag \"{}\". " +
688683
"The default value \"{}\" for \"{}\" is being returned.",
689684
userId, featureKey, variableValue, variableKey
690685
);
691686
}
692687

693-
return variableValue;
688+
Object convertedValue = convertStringToType(variableValue, variableType);
689+
690+
DecisionNotification decisionNotification = DecisionNotification.newFeatureVariableBuilder()
691+
.withUserId(userId)
692+
.withAttributes(copiedAttributes)
693+
.withFeatureKey(featureKey)
694+
.withFeatureEnabled(featureEnabled)
695+
.withVariableKey(variableKey)
696+
.withVariableType(variableType)
697+
.withVariableValue(convertedValue)
698+
.withFeatureDecision(featureDecision)
699+
.build();
700+
701+
702+
notificationCenter.sendNotifications(decisionNotification);
703+
704+
return (T) convertedValue;
705+
}
706+
707+
// Helper method which takes type and variable value and convert it to object to use in Listener DecisionInfo object variable value
708+
@VisibleForTesting
709+
Object convertStringToType(String variableValue, FeatureVariable.VariableType type) {
710+
if (variableValue != null) {
711+
switch (type) {
712+
case DOUBLE:
713+
try {
714+
return Double.parseDouble(variableValue);
715+
} catch (NumberFormatException exception) {
716+
logger.error("NumberFormatException while trying to parse \"" + variableValue +
717+
"\" as Double. " + exception);
718+
}
719+
break;
720+
case STRING:
721+
return variableValue;
722+
case BOOLEAN:
723+
return Boolean.parseBoolean(variableValue);
724+
case INTEGER:
725+
try {
726+
return Integer.parseInt(variableValue);
727+
} catch (NumberFormatException exception) {
728+
logger.error("NumberFormatException while trying to parse \"" + variableValue +
729+
"\" as Integer. " + exception.toString());
730+
}
731+
break;
732+
}
733+
}
734+
return null;
694735
}
695736

696737
/**

core-api/src/main/java/com/optimizely/ab/notification/DecisionNotification.java

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package com.optimizely.ab.notification;
1818

19+
1920
import com.optimizely.ab.bucketing.FeatureDecision;
21+
import com.optimizely.ab.config.FeatureVariable;
2022
import com.optimizely.ab.config.Variation;
2123

2224
import javax.annotation.Nonnull;
@@ -191,4 +193,98 @@ public DecisionNotification build() {
191193
decisionInfo);
192194
}
193195
}
194-
}
196+
197+
public static FeatureVariableDecisionNotificationBuilder newFeatureVariableBuilder() {
198+
return new FeatureVariableDecisionNotificationBuilder();
199+
}
200+
201+
public static class FeatureVariableDecisionNotificationBuilder {
202+
203+
public static final String FEATURE_KEY = "featureKey";
204+
public static final String FEATURE_ENABLED = "featureEnabled";
205+
public static final String SOURCE = "source";
206+
public static final String SOURCE_INFO = "sourceInfo";
207+
public static final String EXPERIMENT_KEY = "experimentKey";
208+
public static final String VARIATION_KEY = "variationKey";
209+
public static final String VARIABLE_KEY = "variableKey";
210+
public static final String VARIABLE_TYPE = "variableType";
211+
public static final String VARIABLE_VALUE = "variableValue";
212+
213+
private String featureKey;
214+
private Boolean featureEnabled;
215+
private FeatureDecision featureDecision;
216+
private String variableKey;
217+
private FeatureVariable.VariableType variableType;
218+
private Object variableValue;
219+
private String userId;
220+
private Map<String, ?> attributes;
221+
private Map<String, Object> decisionInfo;
222+
223+
protected FeatureVariableDecisionNotificationBuilder() {
224+
}
225+
226+
public FeatureVariableDecisionNotificationBuilder withUserId(String userId) {
227+
this.userId = userId;
228+
return this;
229+
}
230+
231+
public FeatureVariableDecisionNotificationBuilder withAttributes(Map<String, ?> attributes) {
232+
this.attributes = attributes;
233+
return this;
234+
}
235+
236+
public FeatureVariableDecisionNotificationBuilder withFeatureKey(String featureKey) {
237+
this.featureKey = featureKey;
238+
return this;
239+
}
240+
241+
public FeatureVariableDecisionNotificationBuilder withFeatureEnabled(boolean featureEnabled) {
242+
this.featureEnabled = featureEnabled;
243+
return this;
244+
}
245+
246+
public FeatureVariableDecisionNotificationBuilder withFeatureDecision(FeatureDecision featureDecision) {
247+
this.featureDecision = featureDecision;
248+
return this;
249+
}
250+
251+
public FeatureVariableDecisionNotificationBuilder withVariableKey(String variableKey) {
252+
this.variableKey = variableKey;
253+
return this;
254+
}
255+
256+
public FeatureVariableDecisionNotificationBuilder withVariableType(FeatureVariable.VariableType variableType) {
257+
this.variableType = variableType;
258+
return this;
259+
}
260+
261+
public FeatureVariableDecisionNotificationBuilder withVariableValue(Object variableValue) {
262+
this.variableValue = variableValue;
263+
return this;
264+
}
265+
266+
public DecisionNotification build() {
267+
decisionInfo = new HashMap<>();
268+
decisionInfo.put(FEATURE_KEY, featureKey);
269+
decisionInfo.put(FEATURE_ENABLED, featureEnabled);
270+
decisionInfo.put(VARIABLE_KEY, variableKey);
271+
decisionInfo.put(VARIABLE_TYPE, variableType);
272+
decisionInfo.put(VARIABLE_VALUE, variableValue);
273+
Map<String, String> sourceInfo = new HashMap<>();
274+
if (featureDecision != null && FeatureDecision.DecisionSource.FEATURE_TEST.equals(featureDecision.decisionSource)) {
275+
sourceInfo.put(EXPERIMENT_KEY, featureDecision.experiment.getKey());
276+
sourceInfo.put(VARIATION_KEY, featureDecision.variation.getKey());
277+
decisionInfo.put(SOURCE, featureDecision.decisionSource);
278+
} else {
279+
decisionInfo.put(SOURCE, FeatureDecision.DecisionSource.ROLLOUT);
280+
}
281+
decisionInfo.put(SOURCE_INFO, sourceInfo);
282+
283+
return new DecisionNotification(
284+
NotificationCenter.DecisionNotificationType.FEATURE_VARIABLE.toString(),
285+
userId,
286+
attributes,
287+
decisionInfo);
288+
}
289+
}
290+
}

0 commit comments

Comments
 (0)