Skip to content

Commit bccb148

Browse files
getFeatureVariableValue APIs (#208)
* Feature flag & rollout parsing * New Models added * variation variable usage mapping * Feature variable and its usage parsing * demoTestDatafile version upgraded to 4 and maintain compatibility to v3 * Test cases added for new models * get back to Asynchronous Initialization of OPTLYManager * CR updated * Feature flag & rollout bucketing decision * Decision Service Logic added * Test cases for getVariationForFeatureExperiment * Test cases for getVariationForFeatureRollout * CR updated * fix - file references * replaced experimentId & variationId with their objects in FeatureDecision * isFeatureEnabled API * implemented API with test cases * added a test case for mutex group experiments * Fixed crash with nil check of completion block * Removed xcode analyze warnings * getFeatureVariableValue APIs * APIs implemented * Changed FeatureVariable type enum to NSString * Test cases added * fixed linker reference error and removed warnings * Merge branch 'master' into arafay/getFeatureVariableValue-APIs * Feature flag & rollout parsing (#205) * Feature flag & rollout parsing * New Models added * variation variable usage mapping * Feature variable and its usage parsing * demoTestDatafile version upgraded to 4 and maintain compatibility to v3 * Test cases added for new models * get back to Asynchronous Initialization of OPTLYManager * CR updated * Removed xcode analyze warnings & code covered for OPTLYVariation.h * warnings removed for variable nullability * added test cases for [OPTLYVariation getVariableUsageForVariableId] * Feature flag & rollout bucket decision (#206) * Feature flag & rollout parsing * New Models added * variation variable usage mapping * Feature variable and its usage parsing * demoTestDatafile version upgraded to 4 and maintain compatibility to v3 * Test cases added for new models * get back to Asynchronous Initialization of OPTLYManager * CR updated * Feature flag & rollout bucketing decision * Decision Service Logic added * Test cases for getVariationForFeatureExperiment * Test cases for getVariationForFeatureRollout * CR updated * fix - file references * replaced experimentId & variationId with their objects in FeatureDecision * Removed xcode analyze warnings * copyright years changed * Increase code coverage for Optimizely * replaced macros with categories and static methods * getVariationForFeature revamped for mutex group * increase code coverage for Optimizely * Categories replaced with private static methods * categories replaced with private static methods * fix - OPTLYMacros.h reference in OptimizelySDKUniversal * fix - CI Checks failure * OPTLYFeatureFlag.m+h property "Key" changed to "key" * OPTLYFeatureFlag.m+h property "Key" changed to "key" * method name changed * isFeatureEnabled API (#207) * Feature flag & rollout parsing * New Models added * variation variable usage mapping * Feature variable and its usage parsing * demoTestDatafile version upgraded to 4 and maintain compatibility to v3 * Test cases added for new models * get back to Asynchronous Initialization of OPTLYManager * CR updated * Feature flag & rollout bucketing decision * Decision Service Logic added * Test cases for getVariationForFeatureExperiment * Test cases for getVariationForFeatureRollout * CR updated * fix - file references * replaced experimentId & variationId with their objects in FeatureDecision * isFeatureEnabled API * implemented API with test cases * added a test case for mutex group experiments * Fixed crash with nil check of completion block * Removed xcode analyze warnings * Merge branch 'master' into arafay/isFeatureEnabled-API * Merge branch 'master' into arafay/isFeatureEnabled-API * master: Feature flag & rollout bucket decision (#206) Feature flag & rollout parsing (#205) # Conflicts: # OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.m # OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureDecision.h # OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureDecision.m # OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureFlag.h # OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureFlag.m # OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureVariable.h # OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureVariable.m # OptimizelySDKCore/OptimizelySDKCore/OPTLYLoggerMessages.h # OptimizelySDKCore/OptimizelySDKCore/OPTLYLoggerMessages.m # OptimizelySDKCore/OptimizelySDKCore/OPTLYProjectConfig.m # OptimizelySDKCore/OptimizelySDKCore/OPTLYRollout.h # OptimizelySDKCore/OptimizelySDKCore/OPTLYRollout.m # OptimizelySDKCore/OptimizelySDKCore/OPTLYVariableUsage.h # OptimizelySDKCore/OptimizelySDKCore/OPTLYVariableUsage.m # OptimizelySDKCore/OptimizelySDKCoreTests/OPTLYDecisionServiceTest.m # OptimizelySDKCore/OptimizelySDKCoreTests/OPTLYProjectConfigTest.m # OptimizelySDKCore/OptimizelySDKCoreTests/OptimizelyTest.m # OptimizelySDKCore/OptimizelySDKCoreTests/TestData/optimizely_6372300739_v4.json # OptimizelySDKCore/OptimizelySDKCoreTests/TestData/test_data_10_experiments.json # OptimizelySDKUniversal/OptimizelySDKUniversal.xcodeproj/project.pbxproj * fixes - after merge * warnings removed
1 parent e7698e0 commit bccb148

File tree

13 files changed

+883
-4
lines changed

13 files changed

+883
-4
lines changed

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureFlag.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#import <OptimizelySDKCore/OPTLYJSONModelLib.h>
2222
#endif
2323

24-
@class OPTLYProjectConfig;
24+
@class OPTLYProjectConfig, OPTLYFeatureVariable;
2525
@protocol OPTLYFeatureVariable;
2626
@protocol OPTLYFeatureFlag
2727
@end
@@ -48,4 +48,9 @@
4848
*/
4949
- (BOOL)isValid:(nonnull OPTLYProjectConfig *)config;
5050

51+
/**
52+
* Get Feature Variable object for a key.
53+
*/
54+
- (nullable OPTLYFeatureVariable *)getFeatureVariableForKey:(nonnull NSString *)variableKey;
55+
5156
@end

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureFlag.m

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
#import "OPTLYDatafileKeys.h"
1919
#import "OPTLYProjectConfig.h"
2020
#import "OPTLYExperiment.h"
21+
#import "OPTLYFeatureVariable.h"
22+
23+
@interface OPTLYFeatureFlag()
24+
25+
@property (nonatomic, strong) NSDictionary<NSString *, OPTLYFeatureVariable *><Ignore> *featureVariableKeyToFeatureVariableMap;
26+
27+
@end
2128

2229
@implementation OPTLYFeatureFlag
2330

@@ -51,9 +58,32 @@ - (BOOL)isValid:(OPTLYProjectConfig *)config {
5158
return true;
5259
}
5360

61+
- (OPTLYFeatureVariable *)getFeatureVariableForKey:(NSString *)variableKey {
62+
OPTLYFeatureVariable *featureVariable = self.featureVariableKeyToFeatureVariableMap[variableKey];
63+
return featureVariable;
64+
}
65+
66+
# pragma mark - Helper methods
67+
68+
- (NSDictionary<NSString *, OPTLYFeatureVariable *> *)featureVariableKeyToFeatureVariableMap {
69+
if (!_featureVariableKeyToFeatureVariableMap) {
70+
_featureVariableKeyToFeatureVariableMap = [self generateFeatureVariableKeyToFeatureVariableMap];
71+
}
72+
return _featureVariableKeyToFeatureVariableMap;
73+
}
74+
75+
- (NSDictionary<NSString *, OPTLYFeatureVariable *> *)generateFeatureVariableKeyToFeatureVariableMap {
76+
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
77+
for (OPTLYFeatureVariable *variable in self.variables) {
78+
map[variable.key] = variable;
79+
}
80+
return [NSDictionary dictionaryWithDictionary:map];
81+
}
82+
5483
+ (BOOL)isEmptyArray:(NSObject*)array {
5584
return (!array
5685
|| ![array isKindOfClass:[NSArray class]]
5786
|| (((NSArray *)array).count == 0));
5887
}
88+
5989
@end

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureVariable.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
#import <OptimizelySDKCore/OPTLYJSONModelLib.h>
2222
#endif
2323

24+
extern NSString * const FeatureVariableTypeBoolean;
25+
extern NSString * const FeatureVariableTypeString;
26+
extern NSString * const FeatureVariableTypeInteger;
27+
extern NSString * const FeatureVariableTypeDouble;
28+
2429
@protocol OPTLYFeatureVariable
2530
@end
2631

OptimizelySDKCore/OptimizelySDKCore/OPTLYFeatureVariable.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
#import "OPTLYFeatureVariable.h"
1818
#import "OPTLYDatafileKeys.h"
1919

20+
NSString * const FeatureVariableTypeBoolean = @"boolean";
21+
NSString * const FeatureVariableTypeString = @"string";
22+
NSString * const FeatureVariableTypeInteger = @"integer";
23+
NSString * const FeatureVariableTypeDouble = @"double";
24+
2025
@implementation OPTLYFeatureVariable
2126

2227
+ (OPTLYJSONKeyMapper*)keyMapper

OptimizelySDKCore/OptimizelySDKCore/OPTLYLoggerMessages.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ extern NSString *const OPTLYLoggerMessagesFeatureDisabledFlagKeyInvalid;
3232
extern NSString *const OPTLYLoggerMessagesFeatureDisabled;
3333
extern NSString *const OPTLYLoggerMessagesFeatureEnabledNotExperimented;
3434
extern NSString *const OPTLYLoggerMessagesFeatureEnabled;
35+
extern NSString *const OPTLYLoggerMessagesFeatureVariableValueFlagKeyInvalid;
36+
extern NSString *const OPTLYLoggerMessagesFeatureVariableValueVariableKeyInvalid;
37+
extern NSString *const OPTLYLoggerMessagesFeatureVariableValueUserIdInvalid;
38+
extern NSString *const OPTLYLoggerMessagesFeatureVariableValueVariableInvalid;
39+
extern NSString *const OPTLYLoggerMessagesFeatureVariableValueVariableTypeInvalid;
40+
extern NSString *const OPTLYLoggerMessagesFeatureVariableValueVariableType;
41+
extern NSString *const OPTLYLoggerMessagesFeatureVariableValueNotUsed;
42+
extern NSString *const OPTLYLoggerMessagesFeatureVariableValueNotBucketed;
3543

3644
// ---- Bucketer ----
3745
// debug

OptimizelySDKCore/OptimizelySDKCore/OPTLYLoggerMessages.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@
2828
NSString *const OPTLYLoggerMessagesFeatureDisabled = @"[OPTIMIZELY] Feature flag %@ is not enabled for user %@.";
2929
NSString *const OPTLYLoggerMessagesFeatureEnabledNotExperimented = @"[OPTIMIZELY] The user %@ is not being experimented on feature %@.";
3030
NSString *const OPTLYLoggerMessagesFeatureEnabled = @"[OPTIMIZELY] Feature flag %@ is enabled for user %@.";
31+
NSString *const OPTLYLoggerMessagesFeatureVariableValueFlagKeyInvalid = @"[OPTIMIZELY] Feature flag key must not be empty for feature variable value.";
32+
NSString *const OPTLYLoggerMessagesFeatureVariableValueVariableKeyInvalid = @"[OPTIMIZELY] Variable key must not be empty for feature variable value.";
33+
NSString *const OPTLYLoggerMessagesFeatureVariableValueUserIdInvalid = @"[OPTIMIZELY] User ID must not be empty for feature variable value.";
34+
NSString *const OPTLYLoggerMessagesFeatureVariableValueVariableInvalid = @"[OPTIMIZELY] No feature variable was found for key %@ in feature flag %@.";
35+
NSString *const OPTLYLoggerMessagesFeatureVariableValueVariableTypeInvalid = @"[OPTIMIZELY] Variable is of type %@, but you requested it as type %@.";
36+
NSString *const OPTLYLoggerMessagesFeatureVariableValueVariableType = @"[OPTIMIZELY] Returning variable value %@ for variation %@ of feature flag %@";
37+
NSString *const OPTLYLoggerMessagesFeatureVariableValueNotUsed = @"[OPTIMIZELY] Variable %@ is not used in variation %@, returning default value %@.";
38+
NSString *const OPTLYLoggerMessagesFeatureVariableValueNotBucketed = @"[OPTIMIZELY] User %@ is not in any variation for feature flag %@, returning default value %@.";
3139

3240
// ---- Bucketer ----
3341
// debug

OptimizelySDKCore/OptimizelySDKCore/Optimizely.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,58 @@ extern NSString * _Nonnull const OptimizelyNotificationsUserDictionaryExperiment
133133
*/
134134
- (BOOL)isFeatureEnabled:(nullable NSString *)featureKey userId:(nullable NSString *)userId attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
135135

136+
/**
137+
* Gets boolean feature variable value.
138+
* @param featureKey The key for the feature flag.
139+
* @param variableKey The key for the variable.
140+
* @param userId The user ID to be used for bucketing.
141+
* @param attributes The user's attributes.
142+
* @return BOOL feature variable value.
143+
*/
144+
- (BOOL)getFeatureVariableBoolean:(nullable NSString *)featureKey
145+
variableKey:(nullable NSString *)variableKey
146+
userId:(nullable NSString *)userId
147+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
148+
149+
/**
150+
* Gets double feature variable value.
151+
* @param featureKey The key for the feature flag.
152+
* @param variableKey The key for the variable.
153+
* @param userId The user ID to be used for bucketing.
154+
* @param attributes The user's attributes.
155+
* @return double feature variable value of type double.
156+
*/
157+
- (double)getFeatureVariableDouble:(nullable NSString *)featureKey
158+
variableKey:(nullable NSString *)variableKey
159+
userId:(nullable NSString *)userId
160+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
161+
162+
/**
163+
* Gets integer feature variable value.
164+
* @param featureKey The key for the feature flag.
165+
* @param variableKey The key for the variable.
166+
* @param userId The user ID to be used for bucketing.
167+
* @param attributes The user's attributes.
168+
* @return int feature variable value of type integer.
169+
*/
170+
- (int)getFeatureVariableInteger:(nullable NSString *)featureKey
171+
variableKey:(nullable NSString *)variableKey
172+
userId:(nullable NSString *)userId
173+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
174+
175+
/**
176+
* Gets string feature variable value.
177+
* @param featureKey The key for the feature flag.
178+
* @param variableKey The key for the variable.
179+
* @param userId The user ID to be used for bucketing.
180+
* @param attributes The user's attributes.
181+
* @return NSString feature variable value of type string.
182+
*/
183+
- (NSString *_Nullable)getFeatureVariableString:(nullable NSString *)featureKey
184+
variableKey:(nullable NSString *)variableKey
185+
userId:(nullable NSString *)userId
186+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
187+
136188
#pragma mark - trackEvent methods
137189
/**
138190
* Track an event

OptimizelySDKCore/OptimizelySDKCore/Optimizely.m

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#import "OPTLYFeatureFlag.h"
3535
#import "OPTLYFeatureDecision.h"
3636
#import "OPTLYDecisionService.h"
37+
#import "OPTLYFeatureVariable.h"
38+
#import "OPTLYVariableUsage.h"
3739

3840
NSString *const OptimizelyDidActivateExperimentNotification = @"OptimizelyExperimentActivated";
3941
NSString *const OptimizelyDidTrackEventNotification = @"OptimizelyEventTracked";
@@ -236,6 +238,126 @@ - (BOOL)isFeatureEnabled:(NSString *)featureKey userId:(NSString *)userId attrib
236238
return true;
237239
}
238240

241+
- (NSString *)getFeatureVariableValueForType:(NSString *)variableType
242+
featureKey:(nullable NSString *)featureKey
243+
variableKey:(nullable NSString *)variableKey
244+
userId:(nullable NSString *)userId
245+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes {
246+
if ([Optimizely isEmptyString:featureKey]) {
247+
[self.logger logMessage:OPTLYLoggerMessagesFeatureVariableValueFlagKeyInvalid withLevel:OptimizelyLogLevelError];
248+
return nil;
249+
}
250+
if ([Optimizely isEmptyString:variableKey]) {
251+
[self.logger logMessage:OPTLYLoggerMessagesFeatureVariableValueVariableKeyInvalid withLevel:OptimizelyLogLevelError];
252+
return nil;
253+
}
254+
if ([Optimizely isEmptyString:userId]) {
255+
[self.logger logMessage:OPTLYLoggerMessagesFeatureVariableValueUserIdInvalid withLevel:OptimizelyLogLevelError];
256+
return nil;
257+
}
258+
259+
OPTLYFeatureFlag *featureFlag = [self.config getFeatureFlagForKey:featureKey];
260+
if ([Optimizely isEmptyString:featureFlag.key]) {
261+
return nil;
262+
}
263+
264+
OPTLYFeatureVariable *featureVariable = [featureFlag getFeatureVariableForKey:variableKey];
265+
if (!featureVariable) {
266+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesFeatureVariableValueVariableInvalid, variableKey, featureKey];
267+
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelError];
268+
return nil;
269+
} else if (![featureVariable.type isEqualToString:variableType]) {
270+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesFeatureVariableValueVariableTypeInvalid, featureVariable.type, variableType];
271+
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelError];
272+
return nil;
273+
}
274+
275+
NSString *variableValue = featureVariable.defaultValue;
276+
OPTLYFeatureDecision *decision = [self.decisionService getVariationForFeature:featureFlag userId:userId attributes:attributes];
277+
278+
if (decision) {
279+
OPTLYVariation *variation = decision.variation;
280+
OPTLYVariableUsage *featureVariableUsageInstance = [variation getVariableUsageForVariableId:featureVariable.variableId];
281+
282+
if (featureVariableUsageInstance) {
283+
variableValue = featureVariableUsageInstance.value;
284+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesFeatureVariableValueVariableType, variableValue, variation.variationKey, featureFlag.key];
285+
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelInfo];
286+
} else {
287+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesFeatureVariableValueNotUsed, variableKey, variation.variationKey, variableValue];
288+
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelInfo];
289+
}
290+
} else {
291+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesFeatureVariableValueNotBucketed, userId, featureFlag.key, variableValue];
292+
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelInfo];
293+
}
294+
295+
return variableValue;
296+
}
297+
298+
- (BOOL)getFeatureVariableBoolean:(nullable NSString *)featureKey
299+
variableKey:(nullable NSString *)variableKey
300+
userId:(nullable NSString *)userId
301+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes {
302+
303+
NSString *variableValue = [self getFeatureVariableValueForType:FeatureVariableTypeBoolean
304+
featureKey:featureKey
305+
variableKey:variableKey
306+
userId:userId
307+
attributes:attributes];
308+
BOOL booleanValue = false;
309+
if (variableValue) {
310+
booleanValue = [variableValue boolValue];
311+
}
312+
return booleanValue;
313+
}
314+
315+
- (double)getFeatureVariableDouble:(nullable NSString *)featureKey
316+
variableKey:(nullable NSString *)variableKey
317+
userId:(nullable NSString *)userId
318+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes {
319+
320+
NSString *variableValue = [self getFeatureVariableValueForType:FeatureVariableTypeDouble
321+
featureKey:featureKey
322+
variableKey:variableKey
323+
userId:userId
324+
attributes:attributes];
325+
double doubleValue = 0.0;
326+
if (variableValue) {
327+
doubleValue = [variableValue doubleValue];
328+
}
329+
return doubleValue;
330+
}
331+
332+
333+
- (int)getFeatureVariableInteger:(nullable NSString *)featureKey
334+
variableKey:(nullable NSString *)variableKey
335+
userId:(nullable NSString *)userId
336+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes {
337+
338+
NSString *variableValue = [self getFeatureVariableValueForType:FeatureVariableTypeInteger
339+
featureKey:featureKey
340+
variableKey:variableKey
341+
userId:userId
342+
attributes:attributes];
343+
int intValue = 0;
344+
if (variableValue) {
345+
intValue = [variableValue intValue];
346+
}
347+
return intValue;
348+
}
349+
350+
- (NSString *)getFeatureVariableString:(nullable NSString *)featureKey
351+
variableKey:(nullable NSString *)variableKey
352+
userId:(nullable NSString *)userId
353+
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes {
354+
return [self getFeatureVariableValueForType:FeatureVariableTypeString
355+
featureKey:featureKey
356+
variableKey:variableKey
357+
userId:userId
358+
attributes:attributes];
359+
}
360+
239361
#pragma mark trackEvent methods
240362
- (void)track:(NSString *)eventKey userId:(NSString *)userId
241363
{

0 commit comments

Comments
 (0)