Skip to content

Commit ff4bba1

Browse files
Kroach/oasis 1477 bucketing id2 (#165)
* Add OptimizelyBucketId key name * Fix nit with 'return nil' from BOOL OPTLYClient.m method * bucketingId (legacy) --> hashId, userId --> bucketingId, withUserId --> withBucketingId * Add Acquire bucketingId logic to getVariation:experiment:attributes: * OASIS-1477 Bucketing IDs Feature Implementation Summary: Bucketing IDs Feature Implementation Add OptimizelyBucketId key name Fix nit with 'return nil' from BOOL OPTLYClient.m method bucketingId (legacy) --> hashId, userId --> bucketingId, withUserId --> withBucketingId Add Acquire bucketingId logic to getVariation:experiment:attributes: Add 3 tests and BucketerTestsDatafile2.json Test Plan: 3 new tests specfic to Bucketing IDs PASSED 5 test suites remain PASSED OptimizelyiOSDemoApp still functions PASSED Reviewers: alda JIRA Issues: OASIS-1477 Differential Revision: https://phabricator.optimizely.com/D16868 * 'optimizely_bucketid' --> 'Optimizely Bucketing ID' * Add and use OPTLYLoggerMessagesDecisionServiceSettingTheBucketingID * createUserFeatures:attributes: updated with OptimizelyBucketId and OptimizelyBucketIdEventParam * Add '#pragma mark's to OPTLYEventBuilderTest.m * Remove 2 nonexistent BucketerTestsDatafile2.json references from project.pbxproj * Move BucketerTestsDatafile2.json to better location in pbxproj * Add 2 'Optimizely Bucketing ID' tests to OPTLYEventBuilderTest.m * withBucketingId:userId --> withBucketingId:bucketingId * Repair 2 new OPTLYEventBuilderTest.m tests to pass
1 parent c51a42c commit ff4bba1

File tree

12 files changed

+379
-58
lines changed

12 files changed

+379
-58
lines changed

OptimizelySDKCore/OptimizelySDKCore.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
/* Begin PBXBuildFile section */
1010
242C5560BF8316BF4ECE55C7 /* Pods_OptimizelySDKCoreiOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8766E217ECD3BEA353B080F5 /* Pods_OptimizelySDKCoreiOSTests.framework */; };
1111
2D5457521DC903410095E286 /* Pods_OptimizelySDKCoreiOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D5457511DC903410095E286 /* Pods_OptimizelySDKCoreiOS.framework */; };
12+
3E92800E1F26AD4700214C58 /* BucketerTestsDatafile2.json in Resources */ = {isa = PBXBuildFile; fileRef = 3E92800D1F26AD4700214C58 /* BucketerTestsDatafile2.json */; };
13+
3EE687AA1F312FA30084B375 /* BucketerTestsDatafile2.json in Resources */ = {isa = PBXBuildFile; fileRef = 3E92800D1F26AD4700214C58 /* BucketerTestsDatafile2.json */; };
1214
4FFB468BAFC670CF55514638 /* Pods_OptimizelySDKCoreTVOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A3A368275572C29A76DC80A /* Pods_OptimizelySDKCoreTVOSTests.framework */; };
1315
5E3C0E701DDF7A290025DB85 /* OPTLYVariable.m in Sources */ = {isa = PBXBuildFile; fileRef = 5ECBBE671DDE6A800028FF6B /* OPTLYVariable.m */; };
1416
5E3C0E711DDF7A3C0025DB85 /* OPTLYVariable.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ECBBE661DDE6A800028FF6B /* OPTLYVariable.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -264,6 +266,7 @@
264266
0A6CB89801ECA4E57E2C4771 /* Pods-OptimizelySDKCoreiOSTests.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OptimizelySDKCoreiOSTests.beta.xcconfig"; path = "../Pods/Target Support Files/Pods-OptimizelySDKCoreiOSTests/Pods-OptimizelySDKCoreiOSTests.beta.xcconfig"; sourceTree = "<group>"; };
265267
2191B624058E423153096145 /* Pods-OptimizelySDKCoreTVOSTests.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OptimizelySDKCoreTVOSTests.beta.xcconfig"; path = "../Pods/Target Support Files/Pods-OptimizelySDKCoreTVOSTests/Pods-OptimizelySDKCoreTVOSTests.beta.xcconfig"; sourceTree = "<group>"; };
266268
2D5457511DC903410095E286 /* Pods_OptimizelySDKCoreiOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods_OptimizelySDKCoreiOS.framework; path = "../../../../Library/Developer/Xcode/DerivedData/OptimizelySDK-aiiogbugxrjwkbcfmwnqywdmaqtv/Build/Products/Debug-iphonesimulator/Pods_OptimizelySDKCoreiOS.framework"; sourceTree = "<group>"; };
269+
3E92800D1F26AD4700214C58 /* BucketerTestsDatafile2.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = BucketerTestsDatafile2.json; sourceTree = "<group>"; };
267270
3FBA850033BBEC39D85B617B /* Pods-OptimizelySDKCoreTVOS.rc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OptimizelySDKCoreTVOS.rc.xcconfig"; path = "../Pods/Target Support Files/Pods-OptimizelySDKCoreTVOS/Pods-OptimizelySDKCoreTVOS.rc.xcconfig"; sourceTree = "<group>"; };
268271
4AD98EC23436656DED35EBED /* Pods-OptimizelySDKCoreTVOS.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OptimizelySDKCoreTVOS.beta.xcconfig"; path = "../Pods/Target Support Files/Pods-OptimizelySDKCoreTVOS/Pods-OptimizelySDKCoreTVOS.beta.xcconfig"; sourceTree = "<group>"; };
269272
51B050141BF2E9277B39EEE8 /* Pods-OptimizelySDKCoreiOSTests.rc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OptimizelySDKCoreiOSTests.rc.xcconfig"; path = "../Pods/Target Support Files/Pods-OptimizelySDKCoreiOSTests/Pods-OptimizelySDKCoreiOSTests.rc.xcconfig"; sourceTree = "<group>"; };
@@ -620,6 +623,7 @@
620623
isa = PBXGroup;
621624
children = (
622625
EA2FAB951DC6FDFA00B1D81B /* BucketerTestsDatafile.json */,
626+
3E92800D1F26AD4700214C58 /* BucketerTestsDatafile2.json */,
623627
EA2FAB961DC6FDFA00B1D81B /* datafile_6372300739.json */,
624628
EA2FAB971DC6FDFA00B1D81B /* test_data_10_experiments.json */,
625629
EA2FAB981DC6FDFA00B1D81B /* test_data_25_experiments.json */,
@@ -992,6 +996,7 @@
992996
5E4C07F31DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json in Resources */,
993997
EA2FABC91DC6FDFA00B1D81B /* BucketerTestsDatafile.json in Resources */,
994998
EA2FABD21DC6FDFA00B1D81B /* test_data_25_experiments.json in Resources */,
999+
3EE687AA1F312FA30084B375 /* BucketerTestsDatafile2.json in Resources */,
9951000
);
9961001
runOnlyForDeploymentPostprocessing = 0;
9971002
};
@@ -1006,6 +1011,7 @@
10061011
isa = PBXResourcesBuildPhase;
10071012
buildActionMask = 2147483647;
10081013
files = (
1014+
3E92800E1F26AD4700214C58 /* BucketerTestsDatafile2.json in Resources */,
10091015
EA2FABCD1DC6FDFA00B1D81B /* datafile_6372300739.json in Resources */,
10101016
EA2FABD91DC6FDFA00B1D81B /* validator_whitelisting_test_datafile.json in Resources */,
10111017
EA2FABD61DC6FDFA00B1D81B /* test_data_50_experiments.json in Resources */,

OptimizelySDKCore/OptimizelySDKCore/OPTLYBucketer.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ NS_ASSUME_NONNULL_END
3131
@protocol OPTLYBucketer <NSObject>
3232

3333
/**
34-
* Bucket a user into an experiment.
35-
* @param experiment The experiment in which to bucket the user.
36-
* @param userId The ID of the user. This must be a non-null, non-empty string.
37-
* @return The variation the user was bucketed into.
34+
* Bucket a bucketingId into an experiment.
35+
* @param experiment The experiment in which to bucket the bucketingId.
36+
* @param bucketingId The ID to bucket. This must be a non-null, non-empty string.
37+
* @return The variation the bucketingId was bucketed into.
3838
*/
3939
- (nullable OPTLYVariation *)bucketExperiment:(nonnull OPTLYExperiment *)experiment
40-
withUserId:(nonnull NSString *)userId;
40+
withBucketingId:(nonnull NSString *)bucketingId;
4141

4242
@end
4343

@@ -63,11 +63,11 @@ NS_ASSUME_NONNULL_END
6363

6464
/**
6565
* Generate an ID to be used in Murmur3 hash based on the provided User ID and the ID of the entity the user is bucketed into.
66-
* @param userId The user ID provided into the bucketing API.
66+
* @param bucketingId The bucket ID provided to the bucketing API.
6767
* @param entityId The ID of the entity the user is being bucketed into. ex: OPTLYExperiment.experimentId.
6868
* @return The string to be used in the Murmur3 hash for bucketing.
6969
*/
70-
- (nonnull NSString *)makeBucketingIdFromUserId:(nonnull NSString *)userId
70+
- (nonnull NSString *)makeHashIdFromBucketingId:(nonnull NSString *)bucketingId
7171
andEntityId:(nonnull NSString *)entityId;
7272

7373
@end

OptimizelySDKCore/OptimizelySDKCore/OPTLYBucketer.m

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ - (instancetype)initWithConfig:(OPTLYProjectConfig *)config {
6161
}
6262

6363
- (OPTLYVariation *)bucketExperiment:(OPTLYExperiment *)experiment
64-
withUserId:(NSString *)userId {
64+
withBucketingId:(NSString *)bucketingId {
6565
BOOL ok = YES;
6666
// check for mutex
6767
NSString *groupId = experiment.groupId;
@@ -74,7 +74,7 @@ - (OPTLYVariation *)bucketExperiment:(OPTLYExperiment *)experiment
7474
// do nothing if it is overlapping policy
7575
}
7676
else if ([group.policy isEqualToString:OPTLYBucketerMutexPolicy]) {
77-
OPTLYExperiment *mutexExperiment = [self bucketToExperiment:group withUserId:userId];
77+
OPTLYExperiment *mutexExperiment = [self bucketToExperiment:group withBucketingId:bucketingId];
7878
if (mutexExperiment != experiment) { // check to see if the experiment the user should fall into is the same experiment we are bucketing
7979
ok = NO;
8080
}
@@ -86,20 +86,20 @@ - (OPTLYVariation *)bucketExperiment:(OPTLYExperiment *)experiment
8686

8787
// bucket to variation only if experiment passes Mutex check
8888
if (ok) {
89-
OPTLYVariation *variation = [self bucketToVariation:experiment withUserId:userId];
89+
OPTLYVariation *variation = [self bucketToVariation:experiment withBucketingId:bucketingId];
9090
return variation;
9191
}
9292
else {
9393
// log message if the user is mutually excluded
94-
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesUserMutuallyExcluded, userId, experiment.experimentKey, groupId];
94+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesUserMutuallyExcluded, bucketingId, experiment.experimentKey, groupId];
9595
[self.config.logger logMessage:logMessage withLevel:OptimizelyLogLevelInfo];
9696
return nil;
9797
}
9898
}
9999

100-
- (OPTLYExperiment *)bucketToExperiment:(OPTLYGroup *)group withUserId:(NSString *)userId {
101-
NSString *bucketingId = [self makeBucketingIdFromUserId:userId andEntityId:group.groupId];
102-
int bucketValue = [self generateBucketValue:bucketingId];
100+
- (OPTLYExperiment *)bucketToExperiment:(OPTLYGroup *)group withBucketingId:(NSString *)bucketingId {
101+
NSString *hashId = [self makeHashIdFromBucketingId:bucketingId andEntityId:group.groupId];
102+
int bucketValue = [self generateBucketValue:hashId];
103103

104104
if ([group.trafficAllocations count] == 0) {
105105
// log error if there are no traffic allocation values
@@ -121,7 +121,7 @@ - (OPTLYExperiment *)bucketToExperiment:(OPTLYGroup *)group withUserId:(NSString
121121
code:OPTLYErrorTypesDataUnknown
122122
description:NSLocalizedString(OPTLYErrorHandlerMessagesExperimentUnknown, experimentId)];
123123

124-
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesForcedBucketingFailed, experimentId, userId];
124+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesForcedBucketingFailed, experimentId, bucketingId];
125125
[self.config.logger logMessage:logMessage withLevel:OptimizelyLogLevelError];
126126
}
127127
return experiment;
@@ -136,9 +136,9 @@ - (OPTLYExperiment *)bucketToExperiment:(OPTLYGroup *)group withUserId:(NSString
136136
return nil;
137137
}
138138

139-
- (OPTLYVariation *)bucketToVariation:(OPTLYExperiment *)experiment withUserId: (NSString *)userId {
140-
NSString *bucketingId = [self makeBucketingIdFromUserId:userId andEntityId:experiment.experimentId];
141-
int bucketValue = [self generateBucketValue:bucketingId];
139+
- (OPTLYVariation *)bucketToVariation:(OPTLYExperiment *)experiment withBucketingId: (NSString *)bucketingId {
140+
NSString *hashId = [self makeHashIdFromBucketingId:bucketingId andEntityId:experiment.experimentId];
141+
int bucketValue = [self generateBucketValue:hashId];
142142

143143
if ([experiment.trafficAllocations count] == 0) {
144144
// log error if there are no traffic allocation values
@@ -162,11 +162,11 @@ - (OPTLYVariation *)bucketToVariation:(OPTLYExperiment *)experiment withUserId:
162162
code:OPTLYErrorTypesDataUnknown
163163
description:NSLocalizedString(OPTLYErrorHandlerMessagesVariationUnknown, variationId)];
164164

165-
NSString *variationUnknownMessage = [NSString stringWithFormat:OPTLYLoggerMessagesForcedBucketingFailed, variationId, userId];
165+
NSString *variationUnknownMessage = [NSString stringWithFormat:OPTLYLoggerMessagesForcedBucketingFailed, variationId, bucketingId];
166166
[self.config.logger logMessage:variationUnknownMessage withLevel:OptimizelyLogLevelError];
167167
}
168168
else {
169-
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesBucketAssigned, variation.variationKey, userId];
169+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesBucketAssigned, variation.variationKey, bucketingId];
170170
[self.config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
171171
}
172172
return variation;
@@ -181,21 +181,21 @@ - (OPTLYVariation *)bucketToVariation:(OPTLYExperiment *)experiment withUserId:
181181
return nil;
182182
}
183183

184-
- (int)generateBucketValue:(NSString *)bucketingId {
185-
double ratio = ((double) [self generateUnsignedHashCode32Bit:bucketingId] / (double) MAX_HASH_VALUE);
184+
- (int)generateBucketValue:(NSString *)hashId {
185+
double ratio = ((double) [self generateUnsignedHashCode32Bit:hashId] / (double) MAX_HASH_VALUE);
186186
return ratio * MAX_TRAFFIC_VALUE;
187187
}
188188

189-
- (uint32_t)generateUnsignedHashCode32Bit:(NSString *)bucketingId {
190-
const char *str = [bucketingId UTF8String];
191-
int len = (int)[bucketingId length];
189+
- (uint32_t)generateUnsignedHashCode32Bit:(NSString *)hashId {
190+
const char *str = [hashId UTF8String];
191+
int len = (int)[hashId length];
192192
uint32_t result = 0;
193193
MurmurHash3_x86_32(str, len, self.bucket_seed, &result);
194194
return result;
195195
}
196196

197-
- (NSString *)makeBucketingIdFromUserId:(NSString *)userId andEntityId:(NSString *)entityId {
198-
return [NSString stringWithFormat:BUCKETING_ID_TEMPLATE, userId, entityId];
197+
- (NSString *)makeHashIdFromBucketingId:(NSString *)bucketingId andEntityId:(NSString *)entityId {
198+
return [NSString stringWithFormat:BUCKETING_ID_TEMPLATE, bucketingId, entityId];
199199
}
200200

201201
@end

OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@
2020
#import <JSONModel/JSONModelLib.h>
2121
#endif
2222

23+
// An optional bucketing ID may be provided in attributes via a
24+
// key-value pair
25+
// OptimizelyBucketId : bucketId
26+
// to accomplish decoupling bucketing from user identification so
27+
// that a group of users that have the same bucketing ID are put
28+
// into the same variation.
29+
// A Bucketing ID allows equivalence relation on user IDs. A group
30+
// of users with the same bucketing ID defines an equivalence class
31+
// of user IDs that all map to the same experiment variation.
32+
extern NSString * _Nonnull const OptimizelyBucketId;
33+
2334
@class OPTLYExperiment, OPTLYVariation;
2435

2536
@interface OPTLYDecisionService : JSONModel

OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.m

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121
#import "OPTLYErrorHandler.h"
2222
#import "OPTLYExperiment.h"
2323
#import "OPTLYLogger.h"
24+
#import "OPTLYLoggerMessages.h"
2425
#import "OPTLYProjectConfig.h"
2526
#import "OPTLYUserProfile.h"
2627
#import "OPTLYUserProfileServiceBasic.h"
2728
#import "OPTLYVariation.h"
2829

30+
NSString * _Nonnull const OptimizelyBucketId = @"Optimizely Bucketing ID";
31+
2932
@interface OPTLYDecisionService()
3033
@property (nonatomic, strong) OPTLYProjectConfig *config;
3134
@property (nonatomic, strong) id<OPTLYBucketer> bucketer;
@@ -54,6 +57,22 @@ - (OPTLYVariation *)getVariation:(NSString *)userId
5457
NSString *experimentKey = experiment.experimentKey;
5558
NSString *experimentId = experiment.experimentId;
5659

60+
// Acquire bucketingId .
61+
NSString *bucketingId;
62+
// If the bucketing ID key is defined in attributes, then use that
63+
// in place of the userID for the murmur hash key
64+
if (attributes != nil) {
65+
bucketingId = attributes[OptimizelyBucketId];
66+
}
67+
if (bucketingId != nil) {
68+
[self.config.logger logMessage:[NSString stringWithFormat:OPTLYLoggerMessagesDecisionServiceSettingTheBucketingID,
69+
bucketingId]
70+
withLevel:OptimizelyLogLevelDebug];
71+
} else {
72+
// By default, the bucketing ID should be the user ID .
73+
bucketingId = userId;
74+
}
75+
5776
// ---- check if the experiment is running ----
5877
if (![self isExperimentActive:self.config
5978
experimentKey:experimentKey]) {
@@ -103,7 +122,7 @@ - (OPTLYVariation *)getVariation:(NSString *)userId
103122

104123
// bucket user into a variation
105124
bucketedVariation = [self.bucketer bucketExperiment:experiment
106-
withUserId:userId];
125+
withBucketingId:bucketingId];
107126

108127
if (bucketedVariation) {
109128
[self saveUserProfile:userProfileDict variation:bucketedVariation experiment:experiment userId:userId];

OptimizelySDKCore/OptimizelySDKCore/OPTLYEventBuilder.h

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

2626
// --- Event URLs ----
2727
NS_ASSUME_NONNULL_BEGIN
28+
extern NSString * const OptimizelyBucketIdEventParam;
2829
extern NSString * const OPTLYEventBuilderDecisionTicketEventURL;
2930
extern NSString * const OPTLYEventBuilderEventTicketURL;
3031
NS_ASSUME_NONNULL_END

OptimizelySDKCore/OptimizelySDKCore/OPTLYEventBuilder.m

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import "OPTLYAttribute.h"
1818
#import "OPTLYBucketer.h"
1919
#import "OPTLYDecisionEventTicket.h"
20+
#import "OPTLYDecisionService.h"
2021
#import "OPTLYErrorHandler.h"
2122
#import "OPTLYEvent.h"
2223
#import "OPTLYEventBuilder.h"
@@ -34,6 +35,8 @@
3435
#import "OPTLYProjectConfig.h"
3536
#import "OPTLYVariation.h"
3637

38+
NSString * const OptimizelyBucketIdEventParam = @"optimizely_bucketing_id";
39+
3740
// --- Event URLs ----
3841
NSString * const OPTLYEventBuilderDecisionTicketEventURL = @"https://p13nlog.dz.optimizely.com/log/decision";
3942
NSString * const OPTLYEventBuilderEventTicketURL = @"https://p13nlog.dz.optimizely.com/log/event";
@@ -248,24 +251,32 @@ - (NSArray *)createUserFeatures:(OPTLYProjectConfig *)config
248251
NSString *attributeValue = attributes[attributeKey];
249252
NSString *attributeId = attribute.attributeId;
250253

251-
if ([attributeId length] == 0) {
252-
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAttributeInvalidFormat, attributeKey];
253-
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
254-
continue;
255-
}
256-
257254
if ([attributeValue length] == 0) {
258255
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAttributeValueInvalidFormat, attributeKey];
259256
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
260257
continue;
261258
}
262259

263-
NSDictionary *featureParams = @{ OPTLYEventParameterKeysFeaturesId : attributeId,
264-
OPTLYEventParameterKeysFeaturesName : attributeKey,
265-
OPTLYEventParameterKeysFeaturesType : OPTLYEventFeatureFeatureTypeCustomAttribute,
266-
OPTLYEventParameterKeysFeaturesValue : attributeValue,
267-
OPTLYEventParameterKeysFeaturesShouldIndex : @YES };
268-
260+
NSDictionary *featureParams;
261+
if ([attributeKey isEqualToString:OptimizelyBucketId]) {
262+
// check for reserved attribute OptimizelyBucketId
263+
featureParams = @{ OPTLYEventParameterKeysFeaturesName : OptimizelyBucketIdEventParam,
264+
OPTLYEventParameterKeysFeaturesType : OPTLYEventFeatureFeatureTypeCustomAttribute,
265+
OPTLYEventParameterKeysFeaturesValue : attributeValue,
266+
OPTLYEventParameterKeysFeaturesShouldIndex : @YES };
267+
268+
} else {
269+
if ([attributeId length] == 0) {
270+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAttributeInvalidFormat, attributeKey];
271+
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
272+
continue;
273+
}
274+
featureParams = @{ OPTLYEventParameterKeysFeaturesId : attributeId,
275+
OPTLYEventParameterKeysFeaturesName : attributeKey,
276+
OPTLYEventParameterKeysFeaturesType : OPTLYEventFeatureFeatureTypeCustomAttribute,
277+
OPTLYEventParameterKeysFeaturesValue : attributeValue,
278+
OPTLYEventParameterKeysFeaturesShouldIndex : @YES };
279+
}
269280
if (featureParams) {
270281
[features addObject:featureParams];
271282
}

OptimizelySDKCore/OptimizelySDKCore/OPTLYLoggerMessages.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ extern NSString *const OPTLYLoggerMessagesDecisionServiceUserProfileNotExist;
189189
extern NSString *const OPTLYLoggerMessagesDecisionServiceSavedVariationParseError;
190190
extern NSString *const OPTLYLoggerMessagesDecisionServiceGetVariationParseError;
191191
extern NSString *const OPTLYLoggerMessagesDecisionServiceReplaceBucketEntity;
192+
extern NSString *const OPTLYLoggerMessagesDecisionServiceSettingTheBucketingID;
192193

193194
// ---- HTTP Request Manager ----
194195
// Debug (not through logger handler)

0 commit comments

Comments
 (0)