Skip to content

Commit 163ae8c

Browse files
Bot filtering implementation (#277)
* Bot Filtering implemented * Bot Filtering tested * Copyright headers updated * default case logged for getAttributeIdForKey
1 parent 0eadcb0 commit 163ae8c

20 files changed

+365
-138
lines changed

OptimizelySDKCore/OptimizelySDKCore.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@
217217
5E4C07FC1DFF66B00042B1F8 /* OPTLYNetworkServiceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E4C07FA1DFF66B00042B1F8 /* OPTLYNetworkServiceTest.m */; };
218218
5ECBBE681DDE6A800028FF6B /* OPTLYVariable.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ECBBE661DDE6A800028FF6B /* OPTLYVariable.h */; settings = {ATTRIBUTES = (Public, ); }; };
219219
5ECBBE691DDE6A800028FF6B /* OPTLYVariable.m in Sources */ = {isa = PBXBuildFile; fileRef = 5ECBBE671DDE6A800028FF6B /* OPTLYVariable.m */; };
220+
90855D0020ED254500A97BEC /* OPTLYControlAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 90855CFC20ED237F00A97BEC /* OPTLYControlAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; };
221+
90855D0120ED254600A97BEC /* OPTLYControlAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 90855CFC20ED237F00A97BEC /* OPTLYControlAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; };
222+
90855D0D20ED2E0100A97BEC /* OPTLYControlAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 90855D0420ED2B0200A97BEC /* OPTLYControlAttributes.m */; };
223+
90855D0E20ED2E0300A97BEC /* OPTLYControlAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 90855D0420ED2B0200A97BEC /* OPTLYControlAttributes.m */; };
220224
EA064BC71DD3FC8800DF7537 /* OPTLYQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = EA064BC51DD3FC8800DF7537 /* OPTLYQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
221225
EA064BC81DD3FC8800DF7537 /* OPTLYQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = EA064BC51DD3FC8800DF7537 /* OPTLYQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
222226
EA064BC91DD3FC8800DF7537 /* OPTLYQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = EA064BC61DD3FC8800DF7537 /* OPTLYQueue.m */; };
@@ -610,6 +614,8 @@
610614
5ECBBE671DDE6A800028FF6B /* OPTLYVariable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OPTLYVariable.m; sourceTree = "<group>"; };
611615
8766E217ECD3BEA353B080F5 /* Pods_OptimizelySDKCoreiOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OptimizelySDKCoreiOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
612616
8B04E0101E180FAD8C9CA7A6 /* Pods_OptimizelySDKCoreiOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OptimizelySDKCoreiOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
617+
90855CFC20ED237F00A97BEC /* OPTLYControlAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OPTLYControlAttributes.h; path = OptimizelySDKCore/OPTLYControlAttributes.h; sourceTree = "<group>"; };
618+
90855D0420ED2B0200A97BEC /* OPTLYControlAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OPTLYControlAttributes.m; path = OptimizelySDKCore/OPTLYControlAttributes.m; sourceTree = "<group>"; };
613619
924C83E5511EF3E43FC015AF /* Pods-OptimizelySDKCoreTVOSTests.rc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OptimizelySDKCoreTVOSTests.rc.xcconfig"; path = "../Pods/Target Support Files/Pods-OptimizelySDKCoreTVOSTests/Pods-OptimizelySDKCoreTVOSTests.rc.xcconfig"; sourceTree = "<group>"; };
614620
93AEC5364770062DEE863663 /* Pods-OptimizelySDKCoreTVOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OptimizelySDKCoreTVOSTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-OptimizelySDKCoreTVOSTests/Pods-OptimizelySDKCoreTVOSTests.release.xcconfig"; sourceTree = "<group>"; };
615621
9A3A368275572C29A76DC80A /* Pods_OptimizelySDKCoreTVOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OptimizelySDKCoreTVOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1260,6 +1266,8 @@
12601266
EA87A0271DE3142A002E9EF7 /* UserProfileService */ = {
12611267
isa = PBXGroup;
12621268
children = (
1269+
90855CFC20ED237F00A97BEC /* OPTLYControlAttributes.h */,
1270+
90855D0420ED2B0200A97BEC /* OPTLYControlAttributes.m */,
12631271
EA16D9401ECCC5C600C4C998 /* OPTLYDecisionService.h */,
12641272
EA16D9411ECCC5C600C4C998 /* OPTLYDecisionService.m */,
12651273
3E44F6471FEAA22F0044C005 /* OPTLYFeatureDecision.h */,
@@ -1290,6 +1298,7 @@
12901298
buildActionMask = 2147483647;
12911299
files = (
12921300
EA16D9421ECCC5C600C4C998 /* OPTLYDecisionService.h in Headers */,
1301+
90855D0020ED254500A97BEC /* OPTLYControlAttributes.h in Headers */,
12931302
3E858C6F1F4227CD00D53856 /* OPTLYJSONModelClassProperty.h in Headers */,
12941303
3E858C851F4227E300D53856 /* OPTLYJSONValueTransformer.h in Headers */,
12951304
3E44F64A1FEAA2340044C005 /* OPTLYFeatureDecision.h in Headers */,
@@ -1353,6 +1362,7 @@
13531362
buildActionMask = 2147483647;
13541363
files = (
13551364
EA16D9431ECCC5C600C4C998 /* OPTLYDecisionService.h in Headers */,
1365+
90855D0120ED254600A97BEC /* OPTLYControlAttributes.h in Headers */,
13561366
3E858C871F4227ED00D53856 /* OPTLYJSONModel.h in Headers */,
13571367
3E858C8B1F4227F900D53856 /* OPTLYJSONModelError.h in Headers */,
13581368
3E44F64C1FEAA2340044C005 /* OPTLYFeatureDecision.h in Headers */,
@@ -1916,6 +1926,7 @@
19161926
EA2FAC1B1DC6FFC600B1D81B /* OPTLYEventView.m in Sources */,
19171927
EA16D93E1ECBD90E00C4C998 /* OPTLYExperimentBucketMapEntity.m in Sources */,
19181928
EA2FAC1C1DC6FFC600B1D81B /* OPTLYExperiment.m in Sources */,
1929+
90855D0D20ED2E0100A97BEC /* OPTLYControlAttributes.m in Sources */,
19191930
EA064BC91DD3FC8800DF7537 /* OPTLYQueue.m in Sources */,
19201931
EA2FAC1D1DC6FFC600B1D81B /* OPTLYGroup.m in Sources */,
19211932
EA2FAC1E1DC6FFC600B1D81B /* OPTLYProjectConfig.m in Sources */,
@@ -2005,6 +2016,7 @@
20052016
EA2FABF71DC6FFA100B1D81B /* OPTLYExperiment.m in Sources */,
20062017
EA16D93F1ECBD90E00C4C998 /* OPTLYExperimentBucketMapEntity.m in Sources */,
20072018
EA064BCA1DD3FC8800DF7537 /* OPTLYQueue.m in Sources */,
2019+
90855D0E20ED2E0300A97BEC /* OPTLYControlAttributes.m in Sources */,
20082020
EA2FABF81DC6FFA100B1D81B /* OPTLYGroup.m in Sources */,
20092021
EA2FABF91DC6FFA100B1D81B /* OPTLYProjectConfig.m in Sources */,
20102022
EA2FABFA1DC6FFA100B1D81B /* OPTLYTrafficAllocation.m in Sources */,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/****************************************************************************
2+
* Copyright 2018, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
// An optional bucketing ID may be provided in attributes via a
20+
// key-value pair
21+
// OptimizelyBucketId : bucketId
22+
// to accomplish decoupling bucketing from user identification so
23+
// that a group of users that have the same bucketing ID are put
24+
// into the same variation.
25+
// A Bucketing ID allows equivalence relation on user IDs. A group
26+
// of users with the same bucketing ID defines an equivalence class
27+
// of user IDs that all map to the same experiment variation.
28+
extern NSString * _Nonnull const OptimizelyBucketId;
29+
extern NSString * _Nonnull const OptimizelyBotFiltering;
30+
extern NSString * _Nonnull const OptimizelyUserAgent;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/****************************************************************************
2+
* Copyright 2018, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
#import "OPTLYControlAttributes.h"
18+
19+
NSString * _Nonnull const OptimizelyBucketId = @"$opt_bucketing_id";
20+
NSString * _Nonnull const OptimizelyBotFiltering = @"$opt_bot_filtering";
21+
NSString * _Nonnull const OptimizelyUserAgent = @"$opt_user_agent";

OptimizelySDKCore/OptimizelySDKCore/OPTLYDatafileKeys.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2017, Optimizely, Inc. and contributors *
2+
* Copyright 2017-2018, 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. *
@@ -25,7 +25,6 @@ extern NSString * const OPTLYDatafileKeysAccountId;
2525
extern NSString * const OPTLYDatafileKeysProjectId;
2626
extern NSString * const OPTLYDatafileKeysVersion;
2727
extern NSString * const OPTLYDatafileKeysRevision;
28-
extern NSString * const OPTLYDatafileKeysAnonymizeIP;
2928
extern NSString * const OPTLYDatafileKeysExperiments;
3029
extern NSString * const OPTLYDatafileKeysEvents;
3130
extern NSString * const OPTLYDatafileKeysAudiences;

OptimizelySDKCore/OptimizelySDKCore/OPTLYDatafileKeys.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2017, Optimizely, Inc. and contributors *
2+
* Copyright 2017-2018, 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,6 @@
2121
NSString * const OPTLYDatafileKeysProjectId = @"projectId";
2222
NSString * const OPTLYDatafileKeysVersion = @"version";
2323
NSString * const OPTLYDatafileKeysRevision = @"revision";
24-
NSString * const OPTLYDatafileKeysAnonymizeIP = @"anonymizeIP";
2524
NSString * const OPTLYDatafileKeysExperiments = @"experiments";
2625
NSString * const OPTLYDatafileKeysEvents = @"events";
2726
NSString * const OPTLYDatafileKeysAudiences = @"audiences";

OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,6 @@
2020
#import <OptimizelySDKCore/OPTLYJSONModelLib.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-
3423
@class OPTLYExperiment, OPTLYVariation, OPTLYFeatureFlag, OPTLYFeatureDecision;
3524

3625
@interface OPTLYDecisionService : OPTLYJSONModel

OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
#import "OPTLYRollout.h"
3131
#import "OPTLYFeatureDecision.h"
3232
#import "OPTLYGroup.h"
33-
34-
NSString * _Nonnull const OptimizelyBucketId = @"$opt_bucketing_id";
33+
#import "OPTLYControlAttributes.h"
3534

3635
@interface OPTLYDecisionService()
3736
@property (nonatomic, strong) OPTLYProjectConfig *config;

OptimizelySDKCore/OptimizelySDKCore/OPTLYEventBuilder.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2016, Optimizely, Inc. and contributors *
2+
* Copyright 2016,2018, 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. *
@@ -25,7 +25,6 @@
2525

2626
// --- Event URLs ----
2727
NS_ASSUME_NONNULL_BEGIN
28-
extern NSString * const OptimizelyBucketIdEventParam;
2928
extern NSString * const OptimizelyActivateEventKey;
3029
extern NSString * const OPTLYEventBuilderEventsTicketURL;
3130
NS_ASSUME_NONNULL_END

OptimizelySDKCore/OptimizelySDKCore/OPTLYEventBuilder.m

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2016, Optimizely, Inc. and contributors *
2+
* Copyright 2016,2018, 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. *
@@ -32,8 +32,8 @@
3232
#import "OPTLYLogger.h"
3333
#import "OPTLYProjectConfig.h"
3434
#import "OPTLYVariation.h"
35+
#import "OPTLYControlAttributes.h"
3536

36-
NSString * const OptimizelyBucketIdEventParam = @"optimizely_bucketing_id";
3737
NSString * const OptimizelyActivateEventKey = @"campaign_activated";
3838

3939
// --- Event URLs ----
@@ -401,46 +401,40 @@ - (NSArray *)createConversionParams:(OPTLYProjectConfig *)config
401401

402402

403403
- (NSArray *)createUserFeatures:(OPTLYProjectConfig *)config
404-
attributes:(NSDictionary *)attributes
405-
{
404+
attributes:(NSDictionary *)attributes {
405+
406+
NSNumber *botFiltering = config.botFiltering;
406407
NSMutableArray *features = [NSMutableArray new];
407408
NSArray *attributeKeys = [attributes allKeys];
408409

409410
for (NSString *attributeKey in attributeKeys) {
410-
OPTLYAttribute *attribute = [config getAttributeForKey:attributeKey];
411411
NSString *attributeValue = attributes[attributeKey];
412-
NSString *attributeId = attribute.attributeId;
413-
414-
if ([attributeValue length] == 0) {
412+
if ([OPTLYEventBuilderDefault isEmptyString:attributeValue]) {
415413
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAttributeValueInvalidFormat, attributeKey];
416414
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
417415
continue;
418416
}
419-
420-
NSDictionary *featureParams;
421-
if ([attributeKey isEqualToString:OptimizelyBucketId]) {
422-
// check for reserved attribute OptimizelyBucketId
423-
featureParams = @{ OPTLYEventParameterKeysFeaturesId : OptimizelyBucketId,
424-
OPTLYEventParameterKeysFeaturesKey : OptimizelyBucketIdEventParam,
425-
OPTLYEventParameterKeysFeaturesType : OPTLYEventFeatureFeatureTypeCustomAttribute,
426-
OPTLYEventParameterKeysFeaturesValue : attributeValue,
427-
OPTLYEventParameterKeysFeaturesShouldIndex : @YES };
417+
NSString *attributeId = [config getAttributeIdForKey:attributeKey];
418+
if ([OPTLYEventBuilderDefault isEmptyString:attributeId]) {
419+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAttributeInvalidFormat, attributeKey];
420+
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
421+
continue;
428422
} else {
429-
if ([attributeId length] == 0) {
430-
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAttributeInvalidFormat, attributeKey];
431-
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
432-
continue;
433-
}
434-
featureParams = @{ OPTLYEventParameterKeysFeaturesId : attributeId,
435-
OPTLYEventParameterKeysFeaturesKey : attributeKey,
436-
OPTLYEventParameterKeysFeaturesType : OPTLYEventFeatureFeatureTypeCustomAttribute,
437-
OPTLYEventParameterKeysFeaturesValue : attributeValue,
438-
OPTLYEventParameterKeysFeaturesShouldIndex : @YES };
439-
}
440-
if (featureParams) {
441-
[features addObject:featureParams];
423+
[features addObject: @{ OPTLYEventParameterKeysFeaturesId : attributeId,
424+
OPTLYEventParameterKeysFeaturesKey : attributeKey,
425+
OPTLYEventParameterKeysFeaturesType : OPTLYEventFeatureFeatureTypeCustomAttribute,
426+
OPTLYEventParameterKeysFeaturesValue : attributeValue,
427+
OPTLYEventParameterKeysFeaturesShouldIndex : @YES }];
442428
}
443429
}
430+
//check for botFiltering value in the project config file.
431+
if (botFiltering) {
432+
[features addObject:@{ OPTLYEventParameterKeysFeaturesId : OptimizelyBotFiltering,
433+
OPTLYEventParameterKeysFeaturesKey : OptimizelyBotFiltering,
434+
OPTLYEventParameterKeysFeaturesType : OPTLYEventFeatureFeatureTypeCustomAttribute,
435+
OPTLYEventParameterKeysFeaturesValue : @(botFiltering.boolValue),
436+
OPTLYEventParameterKeysFeaturesShouldIndex : @YES }];
437+
}
444438
return [features copy];
445439
}
446440

@@ -460,4 +454,10 @@ + (NSString *)stringOrEmpty:(NSString *)str {
460454
return string;
461455
}
462456

457+
+ (BOOL)isEmptyString:(NSString*)str {
458+
return (str == nil
459+
|| [str isKindOfClass:[NSNull class]]
460+
|| [str length] == 0);
461+
}
462+
463463
@end

OptimizelySDKCore/OptimizelySDKCore/OPTLYLoggerMessages.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ extern NSString *const OPTLYLoggerMessagesGroupUnknownForGroupId;
197197
extern NSString *const OPTLYLoggerMessagesGetVariationNilVariation;
198198
extern NSString *const OPTLYLoggerMessagesVariationKeyUnknownForExperimentKey;
199199
extern NSString *const OPTLYLoggerMessagesProjectConfigUserIdInvalid;
200+
extern NSString *const OPTLYLoggerMessagesAttributeIsReserved;
201+
extern NSString *const OPTLYLoggerMessagesAttributeNotFound;
200202

201203
// ---- User Profile ----
202204
// debug

OptimizelySDKCore/OptimizelySDKCore/OPTLYLoggerMessages.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@
191191
NSString *const OPTLYLoggerMessagesGetVariationNilVariation = @"[PROJECT CONFIG] Get variation returned a nil variation for user %@, experiment %@";
192192
NSString *const OPTLYLoggerMessagesVariationKeyUnknownForExperimentKey = @"[PROJECT CONFIG] Variation key %@ not found for experiment key %@.";
193193
NSString *const OPTLYLoggerMessagesProjectConfigUserIdInvalid = @"[PROJECT CONFIG] User ID cannot be an empty string.";
194+
NSString *const OPTLYLoggerMessagesAttributeIsReserved = @"[PROJECT CONFIG] Attribute %@ unexpectedly has reserved prefix %@; using attribute ID instead of reserved attribute name.";
195+
NSString *const OPTLYLoggerMessagesAttributeNotFound = @"[PROJECT CONFIG] Attribute key %@ is not in datafile.";
194196

195197
// ---- User Profile ----
196198
// debug

0 commit comments

Comments
 (0)