Skip to content

Commit 5a5a48e

Browse files
Decision Listener Implementations (#406)
1 parent 3204317 commit 5a5a48e

11 files changed

+168
-126
lines changed

OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ - (OPTLYFeatureDecision *)getVariationForFeatureGroup:(OPTLYFeatureFlag *)featur
215215
logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesDecisionServiceUserInVariation, userId, variation.variationKey, experiment.experimentKey];
216216
decision = [[OPTLYFeatureDecision alloc] initWithExperiment:experiment
217217
variation:variation
218-
source:DecisionSource.Experiment];
218+
source:DecisionSource.FeatureTest];
219219
}
220220
}
221221
} else {
@@ -258,7 +258,7 @@ - (OPTLYFeatureDecision *)getVariationForFeatureExperiment:(OPTLYFeatureFlag *)f
258258

259259
OPTLYFeatureDecision *decision = [[OPTLYFeatureDecision alloc] initWithExperiment:experiment
260260
variation:variation
261-
source:DecisionSource.Experiment];
261+
source:DecisionSource.FeatureTest];
262262
return decision;
263263
}
264264
}

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureDecision.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@class OPTLYExperiment, OPTLYVariation;
2020

2121
struct DecisionSourceStruct {
22-
NSString * _Nonnull const Experiment;
22+
NSString * _Nonnull const FeatureTest;
2323
NSString * _Nonnull const Rollout;
2424
};
2525
extern const struct DecisionSourceStruct DecisionSource;

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureDecision.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
#import "OPTLYVariation.h"
2020

2121
const struct DecisionSourceStruct DecisionSource = {
22-
.Experiment = @"EXPERIMENT",
23-
.Rollout = @"ROLLOUT"
22+
.FeatureTest = @"feature-test",
23+
.Rollout = @"rollout"
2424
};
2525

2626
@implementation OPTLYFeatureDecision

OptimizelySDKCore/OptimizelySDKCore/OPTLYNotificationCenter.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,25 @@ struct DecisionInfoStruct {
5959
NSString * _Nonnull const FeatureKey;
6060
NSString * _Nonnull const FeatureEnabledKey;
6161
NSString * _Nonnull const Key;
62-
NSString * _Nonnull const SourceExperimentKey;
63-
NSString * _Nonnull const SourceVariationKey;
6462
NSString * _Nonnull const SourceKey;
63+
NSString * _Nonnull const SourceInfoKey;
6564
NSString * _Nonnull const VariableKey;
6665
NSString * _Nonnull const VariableTypeKey;
6766
NSString * _Nonnull const VariableValueKey;
6867
};
6968
extern const struct DecisionInfoStruct DecisionInfo;
7069

70+
struct ExperimentDecisionInfoStruct {
71+
NSString * _Nonnull const ExperimentKey;
72+
NSString * _Nonnull const VariationKey;
73+
};
74+
extern const struct ExperimentDecisionInfoStruct ExperimentDecisionInfo;
75+
7176
/// Notification decision types.
77+
extern NSString * _Nonnull const OPTLYDecisionTypeABTest;
78+
extern NSString * _Nonnull const OPTLYDecisionTypeFeature;
7279
extern NSString * _Nonnull const OPTLYDecisionTypeFeatureVariable;
73-
extern NSString * _Nonnull const OPTLYDecisionTypeExperiment;
74-
extern NSString * _Nonnull const OPTLYDecisionTypeIsFeatureEnabled;
80+
extern NSString * _Nonnull const OPTLYDecisionTypeFeatureTest;
7581

7682
@interface OPTLYNotificationCenter : NSObject
7783

OptimizelySDKCore/OptimizelySDKCore/OPTLYNotificationCenter.m

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,23 @@
3535
.FeatureKey = @"featureKey",
3636
.FeatureEnabledKey = @"featureEnabled",
3737
.Key = @"decisionInfo",
38-
.SourceExperimentKey = @"sourceExperimentKey",
39-
.SourceVariationKey = @"sourceVariationKey",
4038
.SourceKey = @"source",
39+
.SourceInfoKey = @"sourceInfo",
4140
.VariableKey = @"variableKey",
4241
.VariableTypeKey = @"variableType",
4342
.VariableValueKey = @"variableValue"
4443
};
4544

45+
const struct ExperimentDecisionInfoStruct ExperimentDecisionInfo = {
46+
.ExperimentKey = @"experimentKey",
47+
.VariationKey = @"variationKey"
48+
};
49+
4650
/// Notification decision types.
47-
NSString * _Nonnull const OPTLYDecisionTypeFeatureVariable = @"feature_variable";
48-
NSString * _Nonnull const OPTLYDecisionTypeExperiment = @"experiment";
49-
NSString * _Nonnull const OPTLYDecisionTypeIsFeatureEnabled = @"feature";
51+
NSString * _Nonnull const OPTLYDecisionTypeABTest = @"ab-test";
52+
NSString * _Nonnull const OPTLYDecisionTypeFeature = @"feature";
53+
NSString * _Nonnull const OPTLYDecisionTypeFeatureVariable = @"feature-variable";
54+
NSString * _Nonnull const OPTLYDecisionTypeFeatureTest = @"feature-test";
5055

5156
@interface OPTLYNotificationCenter()
5257

OptimizelySDKCore/OptimizelySDKCore/OPTLYProjectConfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ __attribute((deprecated("Use OPTLYProjectConfig initWithBuilder method instead."
109109
**/
110110
- (nullable NSString *)getExperimentIdForKey:(nonnull NSString *)experimentKey;
111111

112+
/**
113+
* Returns true if experiment belongs to any feature, false otherwise.
114+
**/
115+
- (BOOL)isFeatureExperiment:(nonnull NSString *)experimentId;
116+
112117
/**
113118
* Get a Group object for an Id.
114119
*/

OptimizelySDKCore/OptimizelySDKCore/OPTLYProjectConfig.m

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ @interface OPTLYProjectConfig()
4545
@property (nonatomic, strong) NSDictionary<NSString *, OPTLYExperiment *><Ignore> *experimentKeyToExperimentMap;
4646
@property (nonatomic, strong) NSDictionary<NSString *, OPTLYFeatureFlag *><Ignore> *featureFlagKeyToFeatureFlagMap;
4747
@property (nonatomic, strong) NSDictionary<NSString *, OPTLYRollout *><Ignore> *rolloutIdToRolloutMap;
48+
@property (nonatomic, strong) NSDictionary<NSString *, NSArray *><Ignore> *experimentIdToFeatureIdsMap;
4849
@property (nonatomic, strong) NSDictionary<NSString *, NSString *><Ignore> *experimentKeyToExperimentIdMap;
4950
@property (nonatomic, strong) NSDictionary<NSString *, OPTLYGroup *><Ignore> *groupIdToGroupMap;
5051
@property (nonatomic, strong) NSDictionary<NSString *, OPTLYAttribute *><Ignore> *attributeKeyToAttributeMap;
@@ -262,6 +263,11 @@ - (NSString *)getExperimentIdForKey:(NSString *)experimentKey
262263
return experimentId;
263264
}
264265

266+
- (BOOL)isFeatureExperiment:(NSString *)experimentId
267+
{
268+
return [self.experimentIdToFeatureIdsMap.allKeys containsObject:experimentId];
269+
}
270+
265271
- (OPTLYGroup *)getGroupForGroupId:(NSString *)groupId {
266272
OPTLYGroup *group = self.groupIdToGroupMap[groupId];
267273
if (!group) {
@@ -449,6 +455,14 @@ - (NSDictionary *)eventKeyToEventMap {
449455
return _experimentKeyToExperimentIdMap;
450456
}
451457

458+
- (NSDictionary<NSString *,NSArray *><Ignore> *)experimentIdToFeatureIdsMap
459+
{
460+
if (!_experimentIdToFeatureIdsMap) {
461+
_experimentIdToFeatureIdsMap = [self generateExperimentIdToFeatureIdsMap];
462+
}
463+
return _experimentIdToFeatureIdsMap;
464+
}
465+
452466
- (NSDictionary<NSString *, OPTLYGroup *> *)groupIdToGroupMap {
453467
if (!_groupIdToGroupMap) {
454468
_groupIdToGroupMap = [OPTLYProjectConfig generateGroupIdToGroupMapFromGroupsArray:_groups];
@@ -560,6 +574,24 @@ - (NSDictionary *)generateAttributeToKeyMap
560574
return [map copy];
561575
}
562576

577+
- (NSDictionary<NSString *, NSArray *> *)generateExperimentIdToFeatureIdsMap {
578+
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
579+
for (OPTLYFeatureFlag *featureFlag in self.featureFlags) {
580+
for (NSString *experimentId in featureFlag.experimentIds) {
581+
if ([map.allKeys containsObject:experimentId]) {
582+
NSMutableArray *featureIdsArray = [[NSMutableArray alloc] initWithArray:map[experimentId]];
583+
[featureIdsArray addObject:featureFlag.flagId];
584+
map[experimentId] = [featureIdsArray copy];
585+
}
586+
else {
587+
NSArray *featureIdsArray = @[featureFlag.flagId];
588+
map[experimentId] = featureIdsArray;
589+
}
590+
}
591+
}
592+
return [map copy];
593+
}
594+
563595
+ (NSDictionary<NSString *, OPTLYGroup *> *)generateGroupIdToGroupMapFromGroupsArray:(NSArray<OPTLYGroup *> *) groups{
564596
NSMutableDictionary *map = [[NSMutableDictionary alloc] initWithCapacity:groups.count];
565597
for (OPTLYGroup *group in groups) {

OptimizelySDKCore/OptimizelySDKCore/Optimizely.m

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,19 @@ - (OPTLYVariation *)variation:(NSString *)experimentKey
176176
userId:userId
177177
attributes:attributes
178178
bucketer:self.bucketer];
179+
OPTLYExperiment *experiment = [self.config getExperimentForKey:experimentKey];
180+
NSString *decisionType = [self.config isFeatureExperiment:experiment.experimentId] ? OPTLYDecisionTypeFeatureTest : OPTLYDecisionTypeABTest;
179181

180182
NSMutableDictionary *args = [[NSMutableDictionary alloc] init];
181-
[args setValue:OPTLYDecisionTypeExperiment forKey:OPTLYNotificationDecisionTypeKey];
183+
[args setValue:decisionType forKey:OPTLYNotificationDecisionTypeKey];
182184
[args setValue:userId forKey:OPTLYNotificationUserIdKey];
183185
[args setValue:attributes forKey:OPTLYNotificationAttributesKey];
184186

185187
NSMutableDictionary *decisionInfo = [NSMutableDictionary new];
186-
[decisionInfo setValue:(bucketedVariation.variationKey ? experimentKey : [NSNull null]) forKey:OPTLYNotificationExperimentKey];
187-
[decisionInfo setValue:(bucketedVariation.variationKey ?: [NSNull null]) forKey:OPTLYNotificationVariationKey];
188+
NSMutableDictionary *sourceInfo = [NSMutableDictionary new];
189+
sourceInfo[ExperimentDecisionInfo.ExperimentKey] = experimentKey;
190+
sourceInfo[ExperimentDecisionInfo.VariationKey] = bucketedVariation.variationKey ?: [NSNull null];
191+
decisionInfo = sourceInfo;
188192
[args setValue:decisionInfo forKey:DecisionInfo.Key];
189193

190194
[_notificationCenter sendNotifications:OPTLYNotificationTypeDecision args:args];
@@ -241,15 +245,22 @@ - (BOOL)isFeatureEnabled:(NSString *)featureKey userId:(NSString *)userId attrib
241245
}
242246

243247
OPTLYFeatureDecision *decision = [self.decisionService getVariationForFeature:featureFlag userId:userId attributes:attributes];
248+
244249
NSMutableDictionary *args = [[NSMutableDictionary alloc] init];
250+
[args setValue:OPTLYDecisionTypeFeature forKey:OPTLYNotificationDecisionTypeKey];
251+
[args setValue:userId forKey:OPTLYNotificationUserIdKey];
252+
[args setValue:attributes forKey:OPTLYNotificationAttributesKey];
253+
245254
NSMutableDictionary *decisionInfo = [NSMutableDictionary new];
246-
[decisionInfo setValue:[NSNull null] forKey:DecisionInfo.SourceExperimentKey];
247-
[decisionInfo setValue:[NSNull null] forKey:DecisionInfo.SourceVariationKey];
255+
[decisionInfo setValue:@{} forKey:DecisionInfo.SourceInfoKey];
256+
[decisionInfo setValue:featureKey forKey:DecisionInfo.FeatureKey];
248257

249258
if (decision) {
250-
if ([decision.source isEqualToString:DecisionSource.Experiment]) {
251-
[decisionInfo setValue:decision.experiment.experimentKey forKey:DecisionInfo.SourceExperimentKey];
252-
[decisionInfo setValue:decision.variation.variationKey forKey:DecisionInfo.SourceVariationKey];
259+
if ([decision.source isEqualToString:DecisionSource.FeatureTest]) {
260+
NSMutableDictionary *sourceInfo = [NSMutableDictionary new];
261+
[sourceInfo setValue:decision.experiment.experimentKey forKey:ExperimentDecisionInfo.ExperimentKey];
262+
[sourceInfo setValue:decision.variation.variationKey forKey:ExperimentDecisionInfo.VariationKey];
263+
[decisionInfo setValue:sourceInfo forKey:DecisionInfo.SourceInfoKey];
253264
[self sendImpressionEventFor:decision.experiment
254265
variation:decision.variation
255266
userId:userId
@@ -272,11 +283,6 @@ - (BOOL)isFeatureEnabled:(NSString *)featureKey userId:(NSString *)userId attrib
272283
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelInfo];
273284
}
274285

275-
[args setValue:OPTLYDecisionTypeIsFeatureEnabled forKey:OPTLYNotificationDecisionTypeKey];
276-
[args setValue:userId forKey:OPTLYNotificationUserIdKey];
277-
[args setValue:attributes forKey:OPTLYNotificationAttributesKey];
278-
279-
[decisionInfo setValue:featureKey forKey:DecisionInfo.FeatureKey];
280286
[decisionInfo setValue:[NSNumber numberWithBool:result] forKey:DecisionInfo.FeatureEnabledKey];
281287
[decisionInfo setValue:decision.source forKey:DecisionInfo.SourceKey];
282288
[args setValue:decisionInfo forKey:DecisionInfo.Key];
@@ -322,15 +328,16 @@ - (id)getFeatureVariableValueForType:(NSString *)variableType
322328
}
323329

324330
NSMutableDictionary *decisionInfo = [NSMutableDictionary new];
325-
[decisionInfo setValue:[NSNull null] forKey:DecisionInfo.SourceExperimentKey];
326-
[decisionInfo setValue:[NSNull null] forKey:DecisionInfo.SourceVariationKey];
331+
[decisionInfo setValue:@{} forKey:DecisionInfo.SourceInfoKey];
327332

328333
NSString *variableValue = featureVariable.defaultValue;
329334
OPTLYFeatureDecision *decision = [self.decisionService getVariationForFeature:featureFlag userId:userId attributes:attributes];
330335
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];
336+
if ([decision.source isEqualToString:DecisionSource.FeatureTest]) {
337+
NSMutableDictionary *sourceInfo = [NSMutableDictionary new];
338+
[sourceInfo setValue:decision.experiment.experimentKey forKey:ExperimentDecisionInfo.ExperimentKey];
339+
[sourceInfo setValue:decision.variation.variationKey forKey:ExperimentDecisionInfo.VariationKey];
340+
[decisionInfo setValue:sourceInfo forKey:DecisionInfo.SourceInfoKey];
334341
}
335342
OPTLYVariation *variation = decision.variation;
336343
OPTLYVariableUsage *featureVariableUsage = [variation getVariableUsageForVariableId:featureVariable.variableId];

OptimizelySDKCore/OptimizelySDKCoreTests/OPTLYDecisionServiceTest.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ - (void)testGetVariationForFeatureWithNonMutexGroupAndUserIsBucketed {
810810
OPTLYVariation *expectedVariation = [multiVariateExp getVariationForVariationId:kExperimentMultiVariateVariationId];
811811
OPTLYFeatureDecision *expectedDecision = [[OPTLYFeatureDecision alloc] initWithExperiment:multiVariateExp
812812
variation:expectedVariation
813-
source:DecisionSource.Experiment];
813+
source:DecisionSource.FeatureTest];
814814

815815
id decisionServiceMock = OCMPartialMock(self.decisionService);
816816
OCMStub([decisionServiceMock getVariation:kUserId experiment:multiVariateExp attributes:@{}]).andReturn(expectedVariation);
@@ -831,7 +831,7 @@ - (void)testGetVariationForFeatureWithMutexGroupAndUserIsBucketed {
831831
OPTLYVariation *expectedVariation = mutexExperiment.variations[0];
832832
OPTLYFeatureDecision *expectedDecision = [[OPTLYFeatureDecision alloc] initWithExperiment:mutexExperiment
833833
variation:expectedVariation
834-
source:DecisionSource.Experiment];
834+
source:DecisionSource.FeatureTest];
835835
id decisionServiceMock = OCMPartialMock(self.decisionService);
836836
OCMStub([decisionServiceMock getVariation:kUserId experiment:mutexExperiment attributes:@{}]).andReturn(expectedVariation);
837837

OptimizelySDKCore/OptimizelySDKCoreTests/OPTLYNotificationCenterTest.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,7 @@ - (void) testSendGetFeatureVariableNotification {
303303
XCTAssertEqual(@"booleanVariable", decisionInfo[DecisionInfo.VariableKey]);
304304
XCTAssertEqual(@"booleanSingleVariableFeature", decisionInfo[DecisionInfo.FeatureKey]);
305305
XCTAssertEqual(false, [(NSNumber *)decisionInfo[DecisionInfo.VariableValueKey] boolValue]);
306-
XCTAssertEqualObjects([NSNull null], decisionInfo[DecisionInfo.SourceExperimentKey]);
307-
XCTAssertEqualObjects([NSNull null], decisionInfo[DecisionInfo.SourceVariationKey]);
306+
XCTAssertEqualObjects(@{}, decisionInfo[DecisionInfo.SourceInfoKey]);
308307
}];
309308
[self.optimizely getFeatureVariableBoolean:@"booleanSingleVariableFeature" variableKey:@"booleanVariable" userId:kUserId attributes:nil];
310309
[self.optimizely.notificationCenter clearAllNotificationListeners];

0 commit comments

Comments
 (0)