Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Empty UserId (Activate and Track) #356

Merged
merged 4 commits into from
Jan 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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