Skip to content

Commit 925850a

Browse files
Support for Empty UserId (Activate and Track) (#356)
* Added support for empty userId in track and activate calls. * Test case added for forced variation with empty userId. * Testcase added for track with EmptyUserId.
1 parent 7003a14 commit 925850a

File tree

8 files changed

+65
-16
lines changed

8 files changed

+65
-16
lines changed

OptimizelySDKCore/OptimizelySDKCore/OPTLYBaseCondition.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ -(nullable NSNumber *)evaluateMatchTypeExact:(NSDictionary<NSString *, NSObject
5252
NSObject *userAttribute = [attributes objectForKey:self.name];
5353
NSNumber *success = NULL;
5454

55-
if ([self.value isKindOfClass:[NSString class]] && [userAttribute isKindOfClass:[NSString class]]) {
55+
if ([self.value isValidStringType] && [userAttribute isValidStringType]) {
5656
success = [NSNumber numberWithBool:[self.value isEqual:userAttribute]];
5757
}
5858
else if ([self.value isValidNumericAttributeValue] && [userAttribute isValidNumericAttributeValue]) {

OptimizelySDKCore/OptimizelySDKCore/OPTLYEventBuilder.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ - (NSDictionary *)filterEventTags:(NSDictionary *)eventTags {
190190
id tagValue = eventTags[tagKey];
191191

192192
// only string, long, int, double, float, and booleans are supported
193-
if (![tagValue isKindOfClass:[NSString class]] && ![tagValue isKindOfClass:[NSNumber class]]) {
193+
if (![tagValue isValidStringType] && ![tagValue isKindOfClass:[NSNumber class]]) {
194194
[mutableEventTags removeObjectForKey:tagKey];
195195
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesEventTagValueInvalid, tagKey];
196196
[self.config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];

OptimizelySDKCore/OptimizelySDKCore/OPTLYEventTagUtil.m

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import "OPTLYEventTagUtil.h"
1818
#import "OPTLYEventMetric.h"
1919
#import "OPTLYLogger.h"
20+
#import "OPTLYNSObject+Validation.h"
2021

2122
@implementation OPTLYEventTagUtil
2223

@@ -93,7 +94,7 @@ + (NSNumber *)getRevenueValue:(NSDictionary *)eventTags logger:(id<OPTLYLogger>)
9394
answer = nil;
9495
[logger logMessage:OPTLYLoggerMessagesRevenueValueInvalid withLevel:OptimizelyLogLevelWarning];
9596
}
96-
} else if ([value isKindOfClass:[NSString class]]) {
97+
} else if ([value isValidStringType]) {
9798
// cast strings to long long
9899
answer = @([(NSString*)value longLongValue]);
99100
[logger logMessage:[NSString stringWithFormat:OPTLYLoggerMessagesRevenueValueString, value] withLevel:OptimizelyLogLevelWarning];
@@ -138,7 +139,7 @@ + (NSNumber *)getNumericValue:(NSDictionary *)eventTags logger:(id<OPTLYLogger>)
138139
[logger logMessage:[NSString stringWithFormat:OPTLYLoggerMessagesNumericValueInvalidFloat, value] withLevel:OptimizelyLogLevelWarning];
139140
}
140141
}
141-
} else if ([value isKindOfClass:[NSString class]]) {
142+
} else if ([value isValidStringType]) {
142143
// cast strings to double
143144
double doubleValue = [(NSString*)value doubleValue];
144145
if (isfinite(doubleValue)) {

OptimizelySDKCore/OptimizelySDKCore/OPTLYNSObject+Validation.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ NS_ASSUME_NONNULL_BEGIN
2020

2121
@interface NSObject (Validation)
2222

23+
/**
24+
* Returns if object is a valid string type
25+
*
26+
* @returns A Bool whether object is a valid string type.
27+
**/
28+
- (BOOL)isValidStringType;
29+
2330
/**
2431
* Determines if object is a valid non-empty string type.
2532
*

OptimizelySDKCore/OptimizelySDKCore/OPTLYNSObject+Validation.m

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,17 @@
1919

2020
@implementation NSObject (Validation)
2121

22+
- (BOOL)isValidStringType {
23+
if (self) {
24+
// check value is NSString
25+
return [self isKindOfClass:[NSString class]];
26+
}
27+
return false;
28+
}
29+
2230
- (nullable NSString *)getValidString {
2331
if (self) {
24-
if ([self isKindOfClass:[NSString class]] && ![(NSString *)self isEqualToString:@""]) {
32+
if ([self isValidStringType] && ![(NSString *)self isEqualToString:@""]) {
2533
return (NSString *)self;
2634
}
2735
}
@@ -49,7 +57,7 @@ - (nullable NSDictionary *)getValidDictionary {
4957
- (NSString *)getStringOrEmpty {
5058
NSString *string = @"";
5159
if (self) {
52-
if ([self isKindOfClass:[NSString class]]) {
60+
if ([self isValidStringType]) {
5361
string = [string stringByAppendingString:((NSString *)self)];
5462
}
5563
}
@@ -58,7 +66,7 @@ - (NSString *)getStringOrEmpty {
5866

5967
- (nullable NSArray *)getValidAudienceConditionsArray {
6068
if(self) {
61-
if ([self isKindOfClass:[NSString class]]) {
69+
if ([self isValidStringType]) {
6270
//Check if string is a valid json
6371
NSError *error = nil;
6472
NSData *data = [(NSString *)self dataUsingEncoding:NSUTF8StringEncoding];
@@ -82,7 +90,7 @@ - (nullable NSArray *)getValidAudienceConditionsArray {
8290

8391
- (nullable NSArray *)getValidConditionsArray {
8492
if(self) {
85-
if ([self isKindOfClass:[NSString class]]) {
93+
if ([self isValidStringType]) {
8694
NSError *error = nil;
8795
NSData *data = [(NSString *)self dataUsingEncoding:NSUTF8StringEncoding];
8896
NSArray *conditionsArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
@@ -100,7 +108,7 @@ - (BOOL)isValidAttributeValue {
100108
return false;
101109
}
102110
// check value is NSString
103-
if ([self isKindOfClass:[NSString class]]) {
111+
if ([self isValidStringType]) {
104112
return true;
105113
}
106114
// check value is Boolean
@@ -115,7 +123,7 @@ - (BOOL)isValidAttributeValue {
115123
return false;
116124
}
117125

118-
-(BOOL)isValidBooleanAttributeValue {
126+
- (BOOL)isValidBooleanAttributeValue {
119127
if (self) {
120128
// check value is NSNumber
121129
NSNumber *number = (NSNumber *)self;
@@ -126,7 +134,7 @@ -(BOOL)isValidBooleanAttributeValue {
126134
return false;
127135
}
128136

129-
-(BOOL)isValidNumericAttributeValue {
137+
- (BOOL)isValidNumericAttributeValue {
130138
if (self) {
131139
NSNumber *number = (NSNumber *)self;
132140
// check value is NSNumber

OptimizelySDKCore/OptimizelySDKCore/OPTLYNotificationCenter.m

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#import "OPTLYLogger.h"
2020
#import "OPTLYExperiment.h"
2121
#import "OPTLYVariation.h"
22+
#import "OPTLYNSObject+Validation.h"
2223
#import <objc/runtime.h>
2324

2425
NSString *const OPTLYNotificationExperimentKey = @"experiment";
@@ -149,7 +150,7 @@ - (void)notifyActivateListener:(ActivateListener)listener args:(NSDictionary *)a
149150

150151
NSString *userId = (NSString *)[args objectForKey:OPTLYNotificationUserIdKey];
151152
assert(userId);
152-
assert([userId isKindOfClass:[NSString class]]);
153+
assert([userId isValidStringType]);
153154

154155
NSDictionary *attributes = (NSDictionary *)[args objectForKey:OPTLYNotificationAttributesKey];
155156

@@ -178,11 +179,11 @@ - (void)notifyTrackListener:(TrackListener)listener args:(NSDictionary *)args {
178179

179180
NSString *eventKey = (NSString *)[args objectForKey:OPTLYNotificationEventKey];
180181
assert(eventKey);
181-
assert([eventKey isKindOfClass:[NSString class]]);
182+
assert([eventKey isValidStringType]);
182183

183184
NSString *userId = (NSString *)[args objectForKey:OPTLYNotificationUserIdKey];
184185
assert(userId);
185-
assert([userId isKindOfClass:[NSString class]]);
186+
assert([userId isValidStringType]);
186187

187188
NSDictionary *attributes = (NSDictionary *)[args objectForKey:OPTLYNotificationAttributesKey];
188189
if (attributes != nil && ![attributes isEqual:[NSNull null]]) {

OptimizelySDKCore/OptimizelySDKCore/Optimizely.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ - (OPTLYVariation *)activate:(NSString *)experimentKey
123123
return nil;
124124
}
125125

126-
if ([userId getValidString] == nil) {
126+
if (![userId isValidStringType]) {
127127
NSError *error = [self handleErrorLogsForActivate:OPTLYLoggerMessagesUserIdInvalid ofLevel:OptimizelyLogLevelError];
128128
_callback(error);
129129
return nil;
@@ -434,7 +434,7 @@ - (void)track:(NSString *)eventKey
434434
return;
435435
}
436436

437-
if ([userId getValidString] == nil) {
437+
if (![userId isValidStringType]) {
438438
[self handleErrorLogsForTrack:OPTLYLoggerMessagesUserIdInvalid ofLevel:OptimizelyLogLevelError];
439439
return;
440440
}

OptimizelySDKCore/OptimizelySDKCoreTests/OptimizelyTest.m

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,14 @@ - (void)testVariationWhitelisting {
244244

245245
# pragma mark - Integration Tests
246246

247+
- (void)testOptimizelyActivateWithEmptyUserId {
248+
OPTLYVariation *_variation = [self.optimizely activate:@"testExperimentMultivariate"
249+
userId:@""];
250+
XCTAssertNotNil(_variation);
251+
XCTAssertEqualObjects(@"Feorge", _variation.variationKey);
252+
}
253+
254+
247255
- (void)testOptimizelyActivateWithNoExperiment {
248256
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"getActivatedVariation"];
249257

@@ -423,6 +431,28 @@ - (void)testOptimizelyTrackWithNoUser {
423431
[loggerMock stopMocking];
424432
}
425433

434+
- (void)testOptimizelyTrackWithEmptyUserId {
435+
436+
NSString *eventKey = @"testEvent";
437+
__block NSString *_userId = nil;
438+
__block NSString *notificationEventKey = nil;
439+
__block NSDictionary<NSString *, NSObject *> *actualAttributes;
440+
__block NSDictionary<NSString *, NSObject *> *actualEventTags;
441+
442+
[self.optimizely.notificationCenter addTrackNotificationListener:^(NSString * _Nonnull eventKey, NSString * _Nonnull userId, NSDictionary<NSString *, NSObject *> * _Nonnull attributes, NSDictionary * _Nonnull eventTags, NSDictionary<NSString *,NSObject *> * _Nonnull event) {
443+
_userId = userId;
444+
notificationEventKey = eventKey;
445+
actualAttributes = attributes;
446+
actualEventTags = eventTags;
447+
}];
448+
449+
[self.optimizely track:eventKey userId:@""];
450+
XCTAssertEqualObjects(@"", _userId);
451+
XCTAssertEqual(eventKey, notificationEventKey);
452+
XCTAssertEqualObjects(nil, actualAttributes);
453+
XCTAssertEqualObjects(nil, actualEventTags);
454+
}
455+
426456
- (void)testOptimizelyTrackWithInvalidEvent {
427457

428458
NSString *invalidEventKey = @"invalid";
@@ -1580,6 +1610,8 @@ - (void)testSetForcedVariationWithNullAndEmptyUserId
15801610
XCTAssertTrue([self.optimizely setForcedVariation:kExperimentKeyForFV
15811611
userId:@""
15821612
variationKey:kVariationKeyForFV]);
1613+
OPTLYVariation *variation = [self.optimizely getForcedVariation:kExperimentKeyForFV userId:@""];
1614+
XCTAssertEqualObjects(variation.variationKey, kVariationKeyForFV);
15831615
}
15841616

15851617
- (void)testSetForcedVariationWithInvalidExperimentKey

0 commit comments

Comments
 (0)