Skip to content

Commit 2f31b96

Browse files
(feat): OnDecision Notification Listener for isFeatureEnabled/getEnabledFeatures API (#385)
* 1. onDecision listener implemented for isFeatureEnabled. 2. Removed redundant keys used for validation purposes. * Implemented onDecision listener for getEnabledFeatures. * source_variation_key added in decisionInfo. * Suggested changes in java-script SDK PR's implemented. * Bug fixes. * unit test method names updated. * Implementations according to javascript PR review. * Headers updated. * Logs updated. * Method description updated. * Misspells fixed. * 1. Recommended changes made. 2. Auto-generated files updated. * Minor fixes.
1 parent 0e2f2bf commit 2f31b96

File tree

23 files changed

+1620
-1013
lines changed

23 files changed

+1620
-1013
lines changed

OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.m

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,10 @@ - (OPTLYFeatureDecision *)getVariationForFeature:(OPTLYFeatureFlag *)featureFlag
152152
[self.config.logger logMessage:[NSString stringWithFormat:OPTLYLoggerMessagesDecisionServiceFRUserNotBucketed, userId, featureFlag.key]
153153
withLevel:OptimizelyLogLevelDebug];
154154

155-
return nil;
155+
decision = [[OPTLYFeatureDecision alloc] init];
156+
decision.source = DecisionSource.Rollout;
157+
158+
return decision;
156159
}
157160

158161
# pragma mark - Helper Methods
@@ -212,7 +215,7 @@ - (OPTLYFeatureDecision *)getVariationForFeatureGroup:(OPTLYFeatureFlag *)featur
212215
logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesDecisionServiceUserInVariation, userId, variation.variationKey, experiment.experimentKey];
213216
decision = [[OPTLYFeatureDecision alloc] initWithExperiment:experiment
214217
variation:variation
215-
source:DecisionSourceExperiment];
218+
source:DecisionSource.Experiment];
216219
}
217220
}
218221
} else {
@@ -255,7 +258,7 @@ - (OPTLYFeatureDecision *)getVariationForFeatureExperiment:(OPTLYFeatureFlag *)f
255258

256259
OPTLYFeatureDecision *decision = [[OPTLYFeatureDecision alloc] initWithExperiment:experiment
257260
variation:variation
258-
source:DecisionSourceExperiment];
261+
source:DecisionSource.Experiment];
259262
return decision;
260263
}
261264
}
@@ -304,7 +307,7 @@ - (OPTLYFeatureDecision *)getVariationForFeatureRollout:(OPTLYFeatureFlag *)feat
304307
withLevel:OptimizelyLogLevelDebug];
305308
OPTLYFeatureDecision *decision = [[OPTLYFeatureDecision alloc] initWithExperiment:experiment
306309
variation:variation
307-
source:DecisionSourceRollout];
310+
source:DecisionSource.Rollout];
308311
return decision;
309312
}
310313
// Evaluate fall back rule / last rule now
@@ -314,7 +317,7 @@ - (OPTLYFeatureDecision *)getVariationForFeatureRollout:(OPTLYFeatureFlag *)feat
314317
if (variation && variation.variationKey) {
315318
OPTLYFeatureDecision *decision = [[OPTLYFeatureDecision alloc] initWithExperiment:experiment
316319
variation:variation
317-
source:DecisionSourceRollout];
320+
source:DecisionSource.Rollout];
318321
return decision;
319322
}
320323
}

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureDecision.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
@class OPTLYExperiment, OPTLYVariation;
2020

21-
extern NSString * const DecisionSourceExperiment;
22-
extern NSString * const DecisionSourceRollout;
21+
struct DecisionSourceStruct {
22+
NSString * _Nonnull const Experiment;
23+
NSString * _Nonnull const Rollout;
24+
};
25+
extern const struct DecisionSourceStruct DecisionSource;
2326

2427
/**
2528
* This class determines how the Optimizely SDK will handle exceptions and errors.

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureDecision.m

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2017-2018, Optimizely, Inc. and contributors *
2+
* Copyright 2017-2019, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -18,11 +18,13 @@
1818
#import "OPTLYExperiment.h"
1919
#import "OPTLYVariation.h"
2020

21-
NSString * const DecisionSourceExperiment = @"experiment";
22-
NSString * const DecisionSourceRollout = @"rollout";
21+
const struct DecisionSourceStruct DecisionSource = {
22+
.Experiment = @"EXPERIMENT",
23+
.Rollout = @"ROLLOUT"
24+
};
2325

2426
@implementation OPTLYFeatureDecision
25-
27+
2628
- (instancetype)initWithExperiment:(OPTLYExperiment *)experiment variation:(OPTLYVariation *)variation source:(NSString *)source {
2729
self = [super init];
2830
if (self) {

OptimizelySDKCore/OptimizelySDKCore/OPTLYNotificationCenter.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2018, Optimizely, Inc. and contributors *
2+
* Copyright 2018-2019, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -21,7 +21,8 @@
2121
/// Enum representing notification types.
2222
typedef NS_ENUM(NSUInteger, OPTLYNotificationType) {
2323
OPTLYNotificationTypeActivate,
24-
OPTLYNotificationTypeTrack
24+
OPTLYNotificationTypeTrack,
25+
OPTLYNotificationTypeDecision
2526
};
2627

2728
typedef void (^ActivateListener)(OPTLYExperiment * _Nonnull experiment,
@@ -36,6 +37,11 @@ typedef void (^TrackListener)(NSString * _Nonnull eventKey,
3637
NSDictionary * _Nullable eventTags,
3738
NSDictionary<NSString *,id> * _Nonnull event);
3839

40+
typedef void (^DecisionListener)(NSString * _Nonnull type,
41+
NSString * _Nonnull userId,
42+
NSDictionary<NSString *, id> * _Nullable attributes,
43+
NSDictionary<NSString *,id> * _Nonnull decisionInfo);
44+
3945
typedef void (^GenericListener)(NSDictionary * _Nonnull args);
4046

4147
typedef NSMutableDictionary<NSNumber *, GenericListener > OPTLYNotificationHolder;
@@ -47,6 +53,21 @@ extern NSString * _Nonnull const OPTLYNotificationAttributesKey;
4753
extern NSString * _Nonnull const OPTLYNotificationEventKey;
4854
extern NSString * _Nonnull const OPTLYNotificationEventTagsKey;
4955
extern NSString * _Nonnull const OPTLYNotificationLogEventParamsKey;
56+
extern NSString * _Nonnull const OPTLYNotificationDecisionTypeKey;
57+
58+
struct DecisionInfoStruct {
59+
NSString * _Nonnull const FeatureKey;
60+
NSString * _Nonnull const FeatureEnabledKey;
61+
NSString * _Nonnull const Key;
62+
NSString * _Nonnull const SourceExperimentKey;
63+
NSString * _Nonnull const SourceVariationKey;
64+
NSString * _Nonnull const SourceKey;
65+
NSString * _Nonnull const VariableKey;
66+
};
67+
extern const struct DecisionInfoStruct DecisionInfo;
68+
69+
/// Notification decision types.
70+
extern NSString * _Nonnull const OPTLYDecisionTypeIsFeatureEnabled;
5071

5172
@interface OPTLYNotificationCenter : NSObject
5273

@@ -77,6 +98,14 @@ extern NSString * _Nonnull const OPTLYNotificationLogEventParamsKey;
7798
*/
7899
- (NSInteger)addTrackNotificationListener:(TrackListener _Nonnull )trackListener;
79100

101+
/**
102+
* Add a decision notification listener to the notification center.
103+
*
104+
* @param decisionListener - Notification to add.
105+
* @return the notification id used to remove the notification. It is greater than 0 on success.
106+
*/
107+
- (NSInteger)addDecisionNotificationListener:(nonnull DecisionListener)decisionListener;
108+
80109
/**
81110
* Remove the notification listener based on the notificationId passed back from addNotification.
82111
* @param notificationId the id passed back from add notification.

OptimizelySDKCore/OptimizelySDKCore/OPTLYNotificationCenter.m

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@
2929
NSString * _Nonnull const OPTLYNotificationEventKey = @"eventKey";
3030
NSString * _Nonnull const OPTLYNotificationEventTagsKey = @"eventTags";
3131
NSString * _Nonnull const OPTLYNotificationLogEventParamsKey = @"logEventParams";
32+
NSString * _Nonnull const OPTLYNotificationDecisionTypeKey = @"type";
33+
34+
const struct DecisionInfoStruct DecisionInfo = {
35+
.FeatureKey = @"featureKey",
36+
.FeatureEnabledKey = @"featureEnabled",
37+
.Key = @"decisionInfo",
38+
.SourceExperimentKey = @"sourceExperimentKey",
39+
.SourceVariationKey = @"sourceVariationKey",
40+
.SourceKey = @"source",
41+
.VariableKey = @"variableKey",
42+
};
43+
44+
/// Notification decision types.
45+
NSString * _Nonnull const OPTLYDecisionTypeIsFeatureEnabled = @"feature";
3246

3347
@interface OPTLYNotificationCenter()
3448

@@ -46,7 +60,7 @@ - (instancetype)initWithProjectConfig:(OPTLYProjectConfig *)config {
4660
_notificationId = 1;
4761
_config = config;
4862
_notifications = [NSMutableDictionary new];
49-
for (NSUInteger i = OPTLYNotificationTypeActivate; i <= OPTLYNotificationTypeTrack; i++) {
63+
for (NSUInteger i = OPTLYNotificationTypeActivate; i <= OPTLYNotificationTypeDecision; i++) {
5064
NSNumber *number = [NSNumber numberWithUnsignedInteger:i];
5165
_notifications[number] = [NSMutableDictionary new];
5266
}
@@ -72,6 +86,10 @@ - (NSInteger)addTrackNotificationListener:(TrackListener)trackListener {
7286
return [self addNotification:OPTLYNotificationTypeTrack listener:(GenericListener)trackListener];
7387
}
7488

89+
- (NSInteger)addDecisionNotificationListener:(nonnull DecisionListener)decisionListener {
90+
return [self addNotification:OPTLYNotificationTypeDecision listener:(GenericListener) decisionListener];
91+
}
92+
7593
- (BOOL)removeNotificationListener:(NSUInteger)notificationId {
7694
for (NSNumber *notificationType in _notifications.allKeys) {
7795
OPTLYNotificationHolder *notificationMap = _notifications[notificationType];
@@ -104,6 +122,9 @@ - (void)sendNotifications:(OPTLYNotificationType)type args:(NSDictionary *)args
104122
case OPTLYNotificationTypeTrack:
105123
[self notifyTrackListener:((TrackListener) listener) args:args];
106124
break;
125+
case OPTLYNotificationTypeDecision:
126+
[self notifyDecisionListener:((DecisionListener) listener) args:args];
127+
break;
107128
default:
108129
listener(args);
109130
}
@@ -153,7 +174,6 @@ - (void)notifyActivateListener:(ActivateListener)listener args:(NSDictionary *)a
153174
assert([userId isValidStringType]);
154175

155176
NSDictionary *attributes = (NSDictionary *)[args objectForKey:OPTLYNotificationAttributesKey];
156-
157177
if (attributes != nil && ![attributes isEqual:[NSNull null]]) {
158178
assert([attributes isKindOfClass:[NSDictionary class]]);
159179
}
@@ -202,4 +222,30 @@ - (void)notifyTrackListener:(TrackListener)listener args:(NSDictionary *)args {
202222
listener(eventKey, userId, attributes, eventTags, logEvent);
203223
}
204224

225+
- (void)notifyDecisionListener:(DecisionListener)listener args:(NSDictionary *)args {
226+
227+
if(args.allKeys.count < 3) {
228+
NSString *logMessage = [NSString stringWithFormat:@"Not enough arguments to call %@ for notification callback.", listener];
229+
[_config.logger logMessage:logMessage withLevel:OptimizelyLogLevelError];
230+
return; // Not enough arguments in the array
231+
}
232+
233+
NSString *typeKey = (NSString *)[args objectForKey:OPTLYNotificationDecisionTypeKey];
234+
assert(typeKey);
235+
assert([typeKey isValidStringType]);
236+
237+
NSString *userId = (NSString *)[args objectForKey:OPTLYNotificationUserIdKey];
238+
assert(userId);
239+
assert([userId isValidStringType]);
240+
241+
NSDictionary *attributes = ((NSDictionary *)[args objectForKey:OPTLYNotificationAttributesKey]) ?: @{};
242+
assert([attributes isKindOfClass:[NSDictionary class]]);
243+
244+
NSDictionary *decisionInfo = (NSDictionary *)[args objectForKey:DecisionInfo.Key];
245+
assert(decisionInfo);
246+
assert([decisionInfo isKindOfClass:[NSDictionary class]]);
247+
248+
listener(typeKey, userId, attributes, decisionInfo);
249+
}
250+
205251
@end

OptimizelySDKCore/OptimizelySDKCore/Optimizely.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2017-2018, Optimizely, Inc. and contributors *
2+
* Copyright 2017-2019, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -17,13 +17,6 @@
1717
#import <Foundation/Foundation.h>
1818
#import "OPTLYBuilder.h"
1919

20-
extern NSString * _Nonnull const OptimizelyNotificationsUserDictionaryExperimentKey;
21-
extern NSString * _Nonnull const OptimizelyNotificationsUserDictionaryVariationKey;
22-
extern NSString * _Nonnull const OptimizelyNotificationsUserDictionaryUserIdKey;
23-
extern NSString * _Nonnull const OptimizelyNotificationsUserDictionaryAttributesKey;
24-
extern NSString * _Nonnull const OptimizelyNotificationsUserDictionaryEventNameKey;
25-
extern NSString * _Nonnull const OptimizelyNotificationsUserDictionaryExperimentVariationMappingKey;
26-
2720
@class OPTLYProjectConfig, OPTLYVariation, OPTLYDecisionService, OPTLYNotificationCenter;
2821
@protocol OPTLYBucketer, OPTLYErrorHandler, OPTLYEventBuilder, OPTLYEventDispatcher, OPTLYLogger;
2922

0 commit comments

Comments
 (0)