Skip to content

Commit 3204317

Browse files
(feat): OnDecision Notification Listener for getFeatureVariable API (#387)
* 1. onDecision listener implemented for getFeatureVariable. 2. Removed redundant keys used for validation purposes. * ‘source_experiment_key’ and ‘source_variation_key’ added in decisionInfo. * Suggested changes in java-script SDK PR's implemented. * Unit tests to verify empty attributes by default in decision listener. * bug fixes. * Implementations according to javascript PR review. * Headers updated. * Implementations to send raw variable value in decisionInfo instead of string. * Reverting feature variable default fix. * Method description updated. * Misspells fixed. * 1. Recommended changes made. 2. Auto-generated files updated.
1 parent 43db7f8 commit 3204317

File tree

17 files changed

+718
-48
lines changed

17 files changed

+718
-48
lines changed

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureDecision.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ extern const struct DecisionSourceStruct DecisionSource;
3030
@interface OPTLYFeatureDecision : NSObject
3131

3232
/// an OPTLYExperiment associated with the decision.
33-
@property (nonatomic, strong) OPTLYExperiment *experiment;
33+
@property (nonatomic, strong) OPTLYExperiment * _Nullable experiment;
3434
/// an OPTLYVariation associated with the decision.
35-
@property (nonatomic, strong) OPTLYVariation *variation;
35+
@property (nonatomic, strong) OPTLYVariation * _Nullable variation;
3636
/// an NSString to hold the source of the decision. Either experiment or rollout
37-
@property (nonatomic, strong) NSString *source;
37+
@property (nonatomic, strong) NSString * _Nonnull source;
3838

3939
/*
4040
* Initializes the FeatureDecision with an experiment id, variation id & source.
@@ -44,6 +44,6 @@ extern const struct DecisionSourceStruct DecisionSource;
4444
* @param source The source for which the decision made.
4545
* @return An instance of the FeatureDecision.
4646
*/
47-
- (instancetype)initWithExperiment:(OPTLYExperiment *)experiment variation:(OPTLYVariation *)variation source:(NSString *)source;
47+
- (nonnull instancetype)initWithExperiment:(OPTLYExperiment * _Nullable)experiment variation:(OPTLYVariation * _Nullable)variation source:(NSString * _Nonnull)source;
4848

4949
@end

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureDecision.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
@implementation OPTLYFeatureDecision
2727

28-
- (instancetype)initWithExperiment:(OPTLYExperiment *)experiment variation:(OPTLYVariation *)variation source:(NSString *)source {
28+
- (nonnull instancetype)initWithExperiment:(OPTLYExperiment * _Nullable)experiment variation:(OPTLYVariation * _Nullable)variation source:(NSString * _Nonnull)source {
2929
self = [super init];
3030
if (self) {
3131
_experiment = experiment;

OptimizelySDKCore/OptimizelySDKCore/OPTLYNotificationCenter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,13 @@ struct DecisionInfoStruct {
6363
NSString * _Nonnull const SourceVariationKey;
6464
NSString * _Nonnull const SourceKey;
6565
NSString * _Nonnull const VariableKey;
66+
NSString * _Nonnull const VariableTypeKey;
67+
NSString * _Nonnull const VariableValueKey;
6668
};
6769
extern const struct DecisionInfoStruct DecisionInfo;
6870

6971
/// Notification decision types.
72+
extern NSString * _Nonnull const OPTLYDecisionTypeFeatureVariable;
7073
extern NSString * _Nonnull const OPTLYDecisionTypeExperiment;
7174
extern NSString * _Nonnull const OPTLYDecisionTypeIsFeatureEnabled;
7275

OptimizelySDKCore/OptimizelySDKCore/OPTLYNotificationCenter.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@
3939
.SourceVariationKey = @"sourceVariationKey",
4040
.SourceKey = @"source",
4141
.VariableKey = @"variableKey",
42+
.VariableTypeKey = @"variableType",
43+
.VariableValueKey = @"variableValue"
4244
};
4345

4446
/// Notification decision types.
47+
NSString * _Nonnull const OPTLYDecisionTypeFeatureVariable = @"feature_variable";
4548
NSString * _Nonnull const OPTLYDecisionTypeExperiment = @"experiment";
4649
NSString * _Nonnull const OPTLYDecisionTypeIsFeatureEnabled = @"feature";
4750

OptimizelySDKCore/OptimizelySDKCore/Optimizely.m

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,11 @@ - (BOOL)isFeatureEnabled:(NSString *)featureKey userId:(NSString *)userId attrib
286286
return result;
287287
}
288288

289-
- (NSString *)getFeatureVariableValueForType:(NSString *)variableType
290-
featureKey:(nullable NSString *)featureKey
291-
variableKey:(nullable NSString *)variableKey
292-
userId:(nullable NSString *)userId
293-
attributes:(nullable NSDictionary<NSString *, id> *)attributes {
289+
- (id)getFeatureVariableValueForType:(NSString *)variableType
290+
featureKey:(nullable NSString *)featureKey
291+
variableKey:(nullable NSString *)variableKey
292+
userId:(nullable NSString *)userId
293+
attributes:(nullable NSDictionary<NSString *, id> *)attributes {
294294

295295
NSMutableDictionary<NSString *, NSString *> *inputValues = [[NSMutableDictionary alloc] initWithDictionary:@{
296296
OPTLYNotificationUserIdKey:[self ObjectOrNull:userId],
@@ -321,13 +321,19 @@ - (NSString *)getFeatureVariableValueForType:(NSString *)variableType
321321
return nil;
322322
}
323323

324+
NSMutableDictionary *decisionInfo = [NSMutableDictionary new];
325+
[decisionInfo setValue:[NSNull null] forKey:DecisionInfo.SourceExperimentKey];
326+
[decisionInfo setValue:[NSNull null] forKey:DecisionInfo.SourceVariationKey];
327+
324328
NSString *variableValue = featureVariable.defaultValue;
325329
OPTLYFeatureDecision *decision = [self.decisionService getVariationForFeature:featureFlag userId:userId attributes:attributes];
326-
327330
if (decision) {
331+
if ([decision.source isEqualToString:DecisionSource.Experiment]) {
332+
[decisionInfo setValue:decision.experiment.experimentKey forKey:DecisionInfo.SourceExperimentKey];
333+
[decisionInfo setValue:decision.variation.variationKey forKey:DecisionInfo.SourceVariationKey];
334+
}
328335
OPTLYVariation *variation = decision.variation;
329336
OPTLYVariableUsage *featureVariableUsage = [variation getVariableUsageForVariableId:featureVariable.variableId];
330-
331337
if (featureVariableUsage) {
332338
if (variation.featureEnabled) {
333339
variableValue = featureVariableUsage.value;
@@ -346,23 +352,47 @@ - (NSString *)getFeatureVariableValueForType:(NSString *)variableType
346352
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelInfo];
347353
}
348354

349-
return variableValue;
355+
id finalValue = nil;
356+
if (variableValue) {
357+
if ([variableType isEqualToString:FeatureVariableTypeBoolean]) {
358+
finalValue = [NSNumber numberWithBool:[variableValue boolValue]];
359+
} else if ([variableType isEqualToString:FeatureVariableTypeDouble]) {
360+
finalValue = [NSNumber numberWithDouble:[variableValue doubleValue]];
361+
} else if ([variableType isEqualToString:FeatureVariableTypeInteger]) {
362+
finalValue = [NSNumber numberWithDouble:[variableValue intValue]];
363+
} else if ([variableType isEqualToString:FeatureVariableTypeString]) {
364+
finalValue = variableValue;
365+
}
366+
}
367+
368+
NSMutableDictionary *args = [[NSMutableDictionary alloc] init];
369+
[args setValue:OPTLYDecisionTypeFeatureVariable forKey:OPTLYNotificationDecisionTypeKey];
370+
[args setValue:userId forKey:OPTLYNotificationUserIdKey];
371+
[args setValue:attributes forKey:OPTLYNotificationAttributesKey];
372+
373+
[decisionInfo setValue:featureKey forKey:DecisionInfo.FeatureKey];
374+
[decisionInfo setValue:[NSNumber numberWithBool:decision.variation.featureEnabled] forKey:DecisionInfo.FeatureEnabledKey];
375+
[decisionInfo setValue:variableKey forKey:DecisionInfo.VariableKey];
376+
[decisionInfo setValue:variableType forKey:DecisionInfo.VariableTypeKey];
377+
[decisionInfo setValue:finalValue forKey:DecisionInfo.VariableValueKey];
378+
[decisionInfo setValue:decision.source forKey:DecisionInfo.SourceKey];
379+
[args setValue:decisionInfo forKey:DecisionInfo.Key];
380+
381+
[_notificationCenter sendNotifications:OPTLYNotificationTypeDecision args:args];
382+
383+
return finalValue;
350384
}
351385

352386
- (NSNumber *)getFeatureVariableBoolean:(nullable NSString *)featureKey
353387
variableKey:(nullable NSString *)variableKey
354388
userId:(nullable NSString *)userId
355389
attributes:(nullable NSDictionary<NSString *, id> *)attributes {
356390

357-
NSString *variableValue = [self getFeatureVariableValueForType:FeatureVariableTypeBoolean
391+
NSNumber* booleanValue = [self getFeatureVariableValueForType:FeatureVariableTypeBoolean
358392
featureKey:featureKey
359393
variableKey:variableKey
360394
userId:userId
361395
attributes:attributes];
362-
NSNumber* booleanValue = nil;
363-
if (variableValue) {
364-
booleanValue = @([variableValue boolValue]);
365-
}
366396
return booleanValue;
367397
}
368398

@@ -371,15 +401,11 @@ - (NSNumber *)getFeatureVariableDouble:(nullable NSString *)featureKey
371401
userId:(nullable NSString *)userId
372402
attributes:(nullable NSDictionary<NSString *, id> *)attributes {
373403

374-
NSString *variableValue = [self getFeatureVariableValueForType:FeatureVariableTypeDouble
404+
NSNumber* doubleValue = [self getFeatureVariableValueForType:FeatureVariableTypeDouble
375405
featureKey:featureKey
376406
variableKey:variableKey
377407
userId:userId
378408
attributes:attributes];
379-
NSNumber* doubleValue = nil;
380-
if (variableValue) {
381-
doubleValue = @([variableValue doubleValue]);
382-
}
383409
return doubleValue;
384410
}
385411

@@ -389,15 +415,11 @@ - (NSNumber *)getFeatureVariableInteger:(nullable NSString *)featureKey
389415
userId:(nullable NSString *)userId
390416
attributes:(nullable NSDictionary<NSString *, id> *)attributes {
391417

392-
NSString *variableValue = [self getFeatureVariableValueForType:FeatureVariableTypeInteger
418+
NSNumber* intValue = [self getFeatureVariableValueForType:FeatureVariableTypeInteger
393419
featureKey:featureKey
394420
variableKey:variableKey
395421
userId:userId
396422
attributes:attributes];
397-
NSNumber* intValue = nil;
398-
if (variableValue) {
399-
intValue = @([variableValue intValue]);
400-
}
401423
return intValue;
402424
}
403425

OptimizelySDKCore/OptimizelySDKCoreTests/OPTLYNotificationCenterTest.m

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,11 @@ - (void) testSendIsFeatureEnabledNotification {
278278
- (void) testSendGetEnabledFeaturesNotification {
279279

280280
NSDictionary *_attributes = @{
281-
kAttributeKeyBrowserType : @"firefox",
282-
kAttributeKeyBrowserVersion : @(68.1),
283-
kAttributeKeyBrowserBuildNumber : @(106),
284-
kAttributeKeyBrowserIsDefault : @YES
285-
};
281+
kAttributeKeyBrowserType : @"firefox",
282+
kAttributeKeyBrowserVersion : @(68.1),
283+
kAttributeKeyBrowserBuildNumber : @(106),
284+
kAttributeKeyBrowserIsDefault : @YES
285+
};
286286
__weak typeof(self) weakSelf = self;
287287
[weakSelf.optimizely.notificationCenter addDecisionNotificationListener:^(NSString * _Nonnull type, NSString * _Nonnull userId, NSDictionary<NSString *,id> * _Nullable attributes, NSDictionary<NSString *,id> * _Nonnull decisionInfo) {
288288
XCTAssertEqual(kUserId, userId);
@@ -295,6 +295,21 @@ - (void) testSendGetEnabledFeaturesNotification {
295295
[self.optimizely.notificationCenter clearAllNotificationListeners];
296296
}
297297

298+
- (void) testSendGetFeatureVariableNotification {
299+
300+
__weak typeof(self) weakSelf = self;
301+
[weakSelf.optimizely.notificationCenter addDecisionNotificationListener:^(NSString * _Nonnull type, NSString * _Nonnull userId, NSDictionary<NSString *,id> * _Nullable attributes, NSDictionary<NSString *,id> * _Nonnull decisionInfo) {
302+
XCTAssertEqual(kUserId, userId);
303+
XCTAssertEqual(@"booleanVariable", decisionInfo[DecisionInfo.VariableKey]);
304+
XCTAssertEqual(@"booleanSingleVariableFeature", decisionInfo[DecisionInfo.FeatureKey]);
305+
XCTAssertEqual(false, [(NSNumber *)decisionInfo[DecisionInfo.VariableValueKey] boolValue]);
306+
XCTAssertEqualObjects([NSNull null], decisionInfo[DecisionInfo.SourceExperimentKey]);
307+
XCTAssertEqualObjects([NSNull null], decisionInfo[DecisionInfo.SourceVariationKey]);
308+
}];
309+
[self.optimizely getFeatureVariableBoolean:@"booleanSingleVariableFeature" variableKey:@"booleanVariable" userId:kUserId attributes:nil];
310+
[self.optimizely.notificationCenter clearAllNotificationListeners];
311+
}
312+
298313
- (void) testSendNotificationWithAnyAttributes {
299314
// Add activate notifications.
300315
[_notificationCenter addActivateNotificationListener:_activateNotification];

0 commit comments

Comments
 (0)