Skip to content

Commit

Permalink
Support for Empty UserId (Activate and Track) (#356)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
yasirfolio3 authored and thomaszurkan-optimizely committed Jan 11, 2019
1 parent 7003a14 commit 925850a
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 16 deletions.
2 changes: 1 addition & 1 deletion OptimizelySDKCore/OptimizelySDKCore/OPTLYBaseCondition.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ -(nullable NSNumber *)evaluateMatchTypeExact:(NSDictionary<NSString *, NSObject
NSObject *userAttribute = [attributes objectForKey:self.name];
NSNumber *success = NULL;

if ([self.value isKindOfClass:[NSString class]] && [userAttribute isKindOfClass:[NSString class]]) {
if ([self.value isValidStringType] && [userAttribute isValidStringType]) {
success = [NSNumber numberWithBool:[self.value isEqual:userAttribute]];
}
else if ([self.value isValidNumericAttributeValue] && [userAttribute isValidNumericAttributeValue]) {
Expand Down
2 changes: 1 addition & 1 deletion OptimizelySDKCore/OptimizelySDKCore/OPTLYEventBuilder.m
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ - (NSDictionary *)filterEventTags:(NSDictionary *)eventTags {
id tagValue = eventTags[tagKey];

// only string, long, int, double, float, and booleans are supported
if (![tagValue isKindOfClass:[NSString class]] && ![tagValue isKindOfClass:[NSNumber class]]) {
if (![tagValue isValidStringType] && ![tagValue isKindOfClass:[NSNumber class]]) {
[mutableEventTags removeObjectForKey:tagKey];
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesEventTagValueInvalid, tagKey];
[self.config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
Expand Down
5 changes: 3 additions & 2 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYEventTagUtil.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import "OPTLYEventTagUtil.h"
#import "OPTLYEventMetric.h"
#import "OPTLYLogger.h"
#import "OPTLYNSObject+Validation.h"

@implementation OPTLYEventTagUtil

Expand Down Expand Up @@ -93,7 +94,7 @@ + (NSNumber *)getRevenueValue:(NSDictionary *)eventTags logger:(id<OPTLYLogger>)
answer = nil;
[logger logMessage:OPTLYLoggerMessagesRevenueValueInvalid withLevel:OptimizelyLogLevelWarning];
}
} else if ([value isKindOfClass:[NSString class]]) {
} else if ([value isValidStringType]) {
// cast strings to long long
answer = @([(NSString*)value longLongValue]);
[logger logMessage:[NSString stringWithFormat:OPTLYLoggerMessagesRevenueValueString, value] withLevel:OptimizelyLogLevelWarning];
Expand Down Expand Up @@ -138,7 +139,7 @@ + (NSNumber *)getNumericValue:(NSDictionary *)eventTags logger:(id<OPTLYLogger>)
[logger logMessage:[NSString stringWithFormat:OPTLYLoggerMessagesNumericValueInvalidFloat, value] withLevel:OptimizelyLogLevelWarning];
}
}
} else if ([value isKindOfClass:[NSString class]]) {
} else if ([value isValidStringType]) {
// cast strings to double
double doubleValue = [(NSString*)value doubleValue];
if (isfinite(doubleValue)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Validation)

/**
* Returns if object is a valid string type
*
* @returns A Bool whether object is a valid string type.
**/
- (BOOL)isValidStringType;

/**
* Determines if object is a valid non-empty string type.
*
Expand Down
22 changes: 15 additions & 7 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYNSObject+Validation.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@

@implementation NSObject (Validation)

- (BOOL)isValidStringType {
if (self) {
// check value is NSString
return [self isKindOfClass:[NSString class]];
}
return false;
}

- (nullable NSString *)getValidString {
if (self) {
if ([self isKindOfClass:[NSString class]] && ![(NSString *)self isEqualToString:@""]) {
if ([self isValidStringType] && ![(NSString *)self isEqualToString:@""]) {
return (NSString *)self;
}
}
Expand Down Expand Up @@ -49,7 +57,7 @@ - (nullable NSDictionary *)getValidDictionary {
- (NSString *)getStringOrEmpty {
NSString *string = @"";
if (self) {
if ([self isKindOfClass:[NSString class]]) {
if ([self isValidStringType]) {
string = [string stringByAppendingString:((NSString *)self)];
}
}
Expand All @@ -58,7 +66,7 @@ - (NSString *)getStringOrEmpty {

- (nullable NSArray *)getValidAudienceConditionsArray {
if(self) {
if ([self isKindOfClass:[NSString class]]) {
if ([self isValidStringType]) {
//Check if string is a valid json
NSError *error = nil;
NSData *data = [(NSString *)self dataUsingEncoding:NSUTF8StringEncoding];
Expand All @@ -82,7 +90,7 @@ - (nullable NSArray *)getValidAudienceConditionsArray {

- (nullable NSArray *)getValidConditionsArray {
if(self) {
if ([self isKindOfClass:[NSString class]]) {
if ([self isValidStringType]) {
NSError *error = nil;
NSData *data = [(NSString *)self dataUsingEncoding:NSUTF8StringEncoding];
NSArray *conditionsArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
Expand All @@ -100,7 +108,7 @@ - (BOOL)isValidAttributeValue {
return false;
}
// check value is NSString
if ([self isKindOfClass:[NSString class]]) {
if ([self isValidStringType]) {
return true;
}
// check value is Boolean
Expand All @@ -115,7 +123,7 @@ - (BOOL)isValidAttributeValue {
return false;
}

-(BOOL)isValidBooleanAttributeValue {
- (BOOL)isValidBooleanAttributeValue {
if (self) {
// check value is NSNumber
NSNumber *number = (NSNumber *)self;
Expand All @@ -126,7 +134,7 @@ -(BOOL)isValidBooleanAttributeValue {
return false;
}

-(BOOL)isValidNumericAttributeValue {
- (BOOL)isValidNumericAttributeValue {
if (self) {
NSNumber *number = (NSNumber *)self;
// check value is NSNumber
Expand Down
7 changes: 4 additions & 3 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYNotificationCenter.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import "OPTLYLogger.h"
#import "OPTLYExperiment.h"
#import "OPTLYVariation.h"
#import "OPTLYNSObject+Validation.h"
#import <objc/runtime.h>

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

NSString *userId = (NSString *)[args objectForKey:OPTLYNotificationUserIdKey];
assert(userId);
assert([userId isKindOfClass:[NSString class]]);
assert([userId isValidStringType]);

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

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

NSString *eventKey = (NSString *)[args objectForKey:OPTLYNotificationEventKey];
assert(eventKey);
assert([eventKey isKindOfClass:[NSString class]]);
assert([eventKey isValidStringType]);

NSString *userId = (NSString *)[args objectForKey:OPTLYNotificationUserIdKey];
assert(userId);
assert([userId isKindOfClass:[NSString class]]);
assert([userId isValidStringType]);

NSDictionary *attributes = (NSDictionary *)[args objectForKey:OPTLYNotificationAttributesKey];
if (attributes != nil && ![attributes isEqual:[NSNull null]]) {
Expand Down
4 changes: 2 additions & 2 deletions OptimizelySDKCore/OptimizelySDKCore/Optimizely.m
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ - (OPTLYVariation *)activate:(NSString *)experimentKey
return nil;
}

if ([userId getValidString] == nil) {
if (![userId isValidStringType]) {
NSError *error = [self handleErrorLogsForActivate:OPTLYLoggerMessagesUserIdInvalid ofLevel:OptimizelyLogLevelError];
_callback(error);
return nil;
Expand Down Expand Up @@ -434,7 +434,7 @@ - (void)track:(NSString *)eventKey
return;
}

if ([userId getValidString] == nil) {
if (![userId isValidStringType]) {
[self handleErrorLogsForTrack:OPTLYLoggerMessagesUserIdInvalid ofLevel:OptimizelyLogLevelError];
return;
}
Expand Down
32 changes: 32 additions & 0 deletions OptimizelySDKCore/OptimizelySDKCoreTests/OptimizelyTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ - (void)testVariationWhitelisting {

# pragma mark - Integration Tests

- (void)testOptimizelyActivateWithEmptyUserId {
OPTLYVariation *_variation = [self.optimizely activate:@"testExperimentMultivariate"
userId:@""];
XCTAssertNotNil(_variation);
XCTAssertEqualObjects(@"Feorge", _variation.variationKey);
}


- (void)testOptimizelyActivateWithNoExperiment {
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"getActivatedVariation"];

Expand Down Expand Up @@ -423,6 +431,28 @@ - (void)testOptimizelyTrackWithNoUser {
[loggerMock stopMocking];
}

- (void)testOptimizelyTrackWithEmptyUserId {

NSString *eventKey = @"testEvent";
__block NSString *_userId = nil;
__block NSString *notificationEventKey = nil;
__block NSDictionary<NSString *, NSObject *> *actualAttributes;
__block NSDictionary<NSString *, NSObject *> *actualEventTags;

[self.optimizely.notificationCenter addTrackNotificationListener:^(NSString * _Nonnull eventKey, NSString * _Nonnull userId, NSDictionary<NSString *, NSObject *> * _Nonnull attributes, NSDictionary * _Nonnull eventTags, NSDictionary<NSString *,NSObject *> * _Nonnull event) {
_userId = userId;
notificationEventKey = eventKey;
actualAttributes = attributes;
actualEventTags = eventTags;
}];

[self.optimizely track:eventKey userId:@""];
XCTAssertEqualObjects(@"", _userId);
XCTAssertEqual(eventKey, notificationEventKey);
XCTAssertEqualObjects(nil, actualAttributes);
XCTAssertEqualObjects(nil, actualEventTags);
}

- (void)testOptimizelyTrackWithInvalidEvent {

NSString *invalidEventKey = @"invalid";
Expand Down Expand Up @@ -1580,6 +1610,8 @@ - (void)testSetForcedVariationWithNullAndEmptyUserId
XCTAssertTrue([self.optimizely setForcedVariation:kExperimentKeyForFV
userId:@""
variationKey:kVariationKeyForFV]);
OPTLYVariation *variation = [self.optimizely getForcedVariation:kExperimentKeyForFV userId:@""];
XCTAssertEqualObjects(variation.variationKey, kVariationKeyForFV);
}

- (void)testSetForcedVariationWithInvalidExperimentKey
Expand Down

0 comments on commit 925850a

Please sign in to comment.