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

feat(api): Accepting all types for attributes values #309

Merged
merged 8 commits into from
Oct 23, 2018
2 changes: 1 addition & 1 deletion OptimizelySDKCore/OptimizelySDKCore/OPTLYAudience.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ - (void)setConditionsWithNSString:(NSString *)string {
}
}

- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *,NSString *> *)attributes {
- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSObject *> *)attributes {
for (NSObject<OPTLYCondition> *condition in self.conditions) {
if ([condition evaluateConditionsWithAttributes:attributes]) {
// if user satisfies any conditions, return true.
Expand Down
2 changes: 1 addition & 1 deletion OptimizelySDKCore/OptimizelySDKCore/OPTLYBaseCondition.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
/// Condition type
@property (nonatomic, strong) NSString *type;
/// Condition value
@property (nonatomic, strong) NSString *value;
@property (nonatomic, strong) NSObject *value;

+(BOOL)isBaseConditionJSON:(NSData *)jsonData;

Expand Down
6 changes: 3 additions & 3 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYBaseCondition.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ + (BOOL) isBaseConditionJSON:(NSData *)jsonData {
}
}

- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *,NSString *> *)attributes {
- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSObject *> *)attributes {
if (attributes == nil) {
// if the user did not pass in attributes, return false
return false;
}
else {
// check user attribute value for the condition against our condition value
return [self.value isEqualToString:attributes[self.name]];
// check user attribute value for the condition against our condition value
return [self.value isEqual:attributes[self.name]];
}
}

Expand Down
2 changes: 1 addition & 1 deletion OptimizelySDKCore/OptimizelySDKCore/OPTLYCondition.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
/**
* Evaluate the condition against the user attributes.
*/
- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSString *> *)attributes;
- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSObject *> *)attributes;

@end

Expand Down
6 changes: 3 additions & 3 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYCondition.m
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ @implementation OPTLYCondition

@implementation OPTLYAndCondition

- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *,NSString *> *)attributes {
- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSObject *> *)attributes {
for (NSObject<OPTLYCondition> *condition in self.subConditions) {
// if any of our sub conditions are false
if (![condition evaluateConditionsWithAttributes:attributes]) {
Expand All @@ -131,7 +131,7 @@ - (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *,NSString *> *)

@implementation OPTLYOrCondition

- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *,NSString *> *)attributes {
- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSObject *> *)attributes {
for (NSObject<OPTLYCondition> *condition in self.subConditions) {
// if any of our sub conditions are true
if ([condition evaluateConditionsWithAttributes:attributes]) {
Expand All @@ -147,7 +147,7 @@ - (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *,NSString *> *)

@implementation OPTLYNotCondition

- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *,NSString *> *)attributes {
- (BOOL)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSObject *> *)attributes {
// return the negative of the subcondition
return ![self.subCondition evaluateConditionsWithAttributes:attributes];
}
Expand Down
4 changes: 2 additions & 2 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
*/
- (nullable OPTLYVariation *)getVariation:(nonnull NSString *)userId
experiment:(nonnull OPTLYExperiment *)experiment
attributes:(nullable NSDictionary *)attributes;
attributes:(nullable NSDictionary<NSString *, NSObject *> *)attributes;

/**
* Get a variation the user is bucketed into for the given FeatureFlag
Expand All @@ -65,6 +65,6 @@
*/
- (nullable OPTLYFeatureDecision *)getVariationForFeature:(nonnull OPTLYFeatureFlag *)featureFlag
userId:(nonnull NSString *)userId
attributes:(nullable NSDictionary *)attributes;
attributes:(nullable NSDictionary<NSString *, NSObject *> *)attributes;

@end
16 changes: 8 additions & 8 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYDecisionService.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ - (instancetype) initWithProjectConfig:(OPTLYProjectConfig *)config

- (OPTLYVariation *)getVariation:(NSString *)userId
experiment:(OPTLYExperiment *)experiment
attributes:(NSDictionary *)attributes
attributes:(NSDictionary<NSString *, NSObject *> *)attributes
{
NSDictionary *userProfileDict = nil;
OPTLYVariation *bucketedVariation = nil;
Expand Down Expand Up @@ -125,7 +125,7 @@ - (OPTLYVariation *)getVariation:(NSString *)userId

- (OPTLYFeatureDecision *)getVariationForFeature:(OPTLYFeatureFlag *)featureFlag
userId:(NSString *)userId
attributes:(NSDictionary *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {

//Evaluate in this order:

Expand Down Expand Up @@ -157,7 +157,7 @@ - (OPTLYFeatureDecision *)getVariationForFeature:(OPTLYFeatureFlag *)featureFlag
# pragma mark - Helper Methods

- (NSString *)getBucketingId:(NSString *)userId
attributes:(NSDictionary *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {

// By default, the bucketing ID should be the user ID .
NSString *bucketingId = userId;
Expand Down Expand Up @@ -189,7 +189,7 @@ - (OPTLYExperiment *)getExperimentInGroup:(OPTLYGroup *)group bucketingId:(NSStr
- (OPTLYFeatureDecision *)getVariationForFeatureGroup:(OPTLYFeatureFlag *)featureFlag
groupId:(NSString *)groupId
userId:(NSString *)userId
attributes:(NSDictionary *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {

OPTLYFeatureDecision *decision = nil;
NSString *logMessage = nil;
Expand Down Expand Up @@ -225,7 +225,7 @@ - (OPTLYFeatureDecision *)getVariationForFeatureGroup:(OPTLYFeatureFlag *)featur

- (OPTLYFeatureDecision *)getVariationForFeatureExperiment:(OPTLYFeatureFlag *)featureFlag
userId:(NSString *)userId
attributes:(NSDictionary *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {

NSString *featureFlagKey = featureFlag.key;
NSArray *experimentIds = featureFlag.experimentIds;
Expand Down Expand Up @@ -261,7 +261,7 @@ - (OPTLYFeatureDecision *)getVariationForFeatureExperiment:(OPTLYFeatureFlag *)f

- (OPTLYFeatureDecision *)getVariationForFeatureRollout:(OPTLYFeatureFlag *)featureFlag
userId:(NSString *)userId
attributes:(NSDictionary *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {

NSString *bucketing_id = [self getBucketingId:userId attributes:attributes];
NSString *featureFlagKey = featureFlag.key;
Expand Down Expand Up @@ -447,7 +447,7 @@ - (NSString *)getVariationIdFromUserProfile:(NSDictionary *)userProfileDict
- (BOOL)userPassesTargeting:(OPTLYProjectConfig *)config
experiment:(OPTLYExperiment *)experiment
userId:(NSString *)userId
attributes:(NSDictionary *)attributes
attributes:(NSDictionary<NSString *, NSObject *> *)attributes
{
// check if the user is in the experiment
BOOL isUserInExperiment = [self isUserInExperiment:config experiment:experiment attributes:attributes];
Expand Down Expand Up @@ -476,7 +476,7 @@ - (BOOL)isExperimentActive:(OPTLYProjectConfig *)config

- (BOOL)isUserInExperiment:(OPTLYProjectConfig *)config
experiment:(OPTLYExperiment *)experiment
attributes:(NSDictionary *)attributes
attributes:(NSDictionary<NSString *, NSObject *> *)attributes
{
NSArray *audiences = experiment.audienceIds;

Expand Down
4 changes: 2 additions & 2 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYEventBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ NS_ASSUME_NONNULL_END
- (nullable NSDictionary *)buildImpressionEventForUser:(nonnull NSString *)userId
experiment:(nonnull OPTLYExperiment *)experiment
variation:(nonnull OPTLYVariation *)variation
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
attributes:(nullable NSDictionary<NSString *, NSObject *> *)attributes;

/**
* Create the parameters for a conversion event.
Expand All @@ -59,7 +59,7 @@ NS_ASSUME_NONNULL_END
event:(nonnull OPTLYEvent *)event
decisions:(nonnull NSArray<NSDictionary *> *)decisions
eventTags:(nullable NSDictionary *)eventTags
attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
attributes:(nullable NSDictionary<NSString *, NSObject *> *)attributes;
@end

@interface OPTLYEventBuilderDefault : NSObject<OPTLYEventBuilder>
Expand Down
53 changes: 43 additions & 10 deletions OptimizelySDKCore/OptimizelySDKCore/OPTLYEventBuilder.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ -(instancetype)initWithConfig:(OPTLYProjectConfig *)config {
-(NSDictionary *)buildImpressionEventForUser:(NSString *)userId
experiment:(OPTLYExperiment *)experiment
variation:(OPTLYVariation *)variation
attributes:(NSDictionary<NSString *,NSString *> *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {
if (!self.config) {
return nil;
}
Expand All @@ -71,7 +71,7 @@ -(NSDictionary *)buildConversionEventForUser:(NSString *)userId
event:(OPTLYEvent *)event
decisions:(NSArray<NSDictionary *> *)decisions
eventTags:(NSDictionary *)eventTags
attributes:(NSDictionary<NSString *,NSString *> *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {

if (!self.config) {
return nil;
Expand All @@ -87,7 +87,7 @@ -(NSDictionary *)buildConversionEventForUser:(NSString *)userId
}

- (NSDictionary *)createCommonParamsForUser:(NSString *)userId
attributes:(NSDictionary<NSString *, NSString *> *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {
NSMutableDictionary *commonParams = [NSMutableDictionary new];

NSMutableDictionary *visitor = [NSMutableDictionary new];
Expand Down Expand Up @@ -199,15 +199,15 @@ - (NSDictionary *)filterEventTags:(NSDictionary *)eventTags {
}

- (NSArray *)createUserFeatures:(OPTLYProjectConfig *)config
attributes:(NSDictionary *)attributes {
attributes:(NSDictionary<NSString *, NSObject *> *)attributes {

NSNumber *botFiltering = config.botFiltering;
NSMutableArray *features = [NSMutableArray new];
NSArray *attributeKeys = [attributes allKeys];

for (NSString *attributeKey in attributeKeys) {
NSString *attributeValue = attributes[attributeKey];
if ([OPTLYEventBuilderDefault isEmptyString:attributeValue]) {
NSObject *attributeValue = attributes[attributeKey];
if (![OPTLYEventBuilderDefault isValidAttributeValue:attributeValue]) {
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAttributeValueInvalidFormat, attributeKey];
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
continue;
Expand Down Expand Up @@ -252,10 +252,43 @@ + (NSString *)stringOrEmpty:(NSString *)str {
return string;
}

+ (BOOL)isEmptyString:(NSString*)str {
return (str == nil
|| [str isKindOfClass:[NSNull class]]
|| [str length] == 0);
+ (BOOL)isEmptyString:(NSObject *)str {
return (!str
|| ![str isKindOfClass:[NSString class]]
|| [(NSString *)str isEqualToString:@""]);
}

+ (BOOL)isValidAttributeValue:(NSObject *)value {
// check value is NSObject
if (!value || [value isEqual:[NSNull null]]) {
return false;
}
// check value is NSString
if ([value isKindOfClass:[NSString class]]) {
return true;
}
NSNumber *number = (NSNumber *)value;
// check value is NSNumber
if (number && [number isKindOfClass:[NSNumber class]]) {
const char *objCType = [number objCType];
// check NSNumber is of type int, double, bool
return (strcmp(objCType, @encode(short)) == 0)
|| (strcmp(objCType, @encode(unsigned short)) == 0)
|| (strcmp(objCType, @encode(int)) == 0)
|| (strcmp(objCType, @encode(unsigned int)) == 0)
|| (strcmp(objCType, @encode(long)) == 0)
|| (strcmp(objCType, @encode(unsigned long)) == 0)
|| (strcmp(objCType, @encode(long long)) == 0)
|| (strcmp(objCType, @encode(unsigned long long)) == 0)
|| (strcmp(objCType, @encode(float)) == 0)
|| (strcmp(objCType, @encode(double)) == 0)
|| (strcmp(objCType, @encode(char)) == 0)
|| (strcmp(objCType, @encode(unsigned char)) == 0)
|| (strcmp(objCType, @encode(bool)) == 0)
|| [number isEqual:@YES]
|| [number isEqual:@NO];
}
return false;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ typedef NS_ENUM(NSUInteger, OPTLYNotificationType) {

typedef void (^ActivateListener)(OPTLYExperiment * _Nonnull experiment,
NSString * _Nonnull userId,
NSDictionary<NSString *,NSString *> * _Nonnull attributes,
NSDictionary<NSString *, NSObject *> * _Nonnull attributes,
OPTLYVariation * _Nonnull variation,
NSDictionary<NSString *,NSObject *> * _Nonnull event);

typedef void (^TrackListener)(NSString * _Nonnull eventKey,
NSString * _Nonnull userId,
NSDictionary<NSString *,NSString *> * _Nonnull attributes,
NSDictionary<NSString *, NSObject *> * _Nonnull attributes,
NSDictionary * _Nonnull eventTags,
NSDictionary<NSString *,NSObject *> * _Nonnull event);

Expand Down
2 changes: 1 addition & 1 deletion OptimizelySDKCore/OptimizelySDKCore/OPTLYProjectConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ __attribute((deprecated("Use OPTLYProjectConfig initWithBuilder method instead."
*/
- (nullable OPTLYVariation *)getVariationForExperiment:(nonnull NSString *)experimentKey
userId:(nonnull NSString *)userId
attributes:(nullable NSDictionary<NSString *,NSString *> *)attributes
attributes:(nullable NSDictionary<NSString *, NSObject *> *)attributes
bucketer:(nullable id<OPTLYBucketer>)bucketer;

@end
2 changes: 1 addition & 1 deletion OptimizelySDKCore/OptimizelySDKCore/OPTLYProjectConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ - (NSDictionary *)generateAttributeToKeyMap
// TODO: Remove bucketer from parameters -- this is not needed
- (OPTLYVariation *)getVariationForExperiment:(NSString *)experimentKey
userId:(NSString *)userId
attributes:(NSDictionary<NSString *,NSString *> *)attributes
attributes:(NSDictionary<NSString *, NSObject *> *)attributes
bucketer:(id<OPTLYBucketer>)bucketer
{
OPTLYExperiment *experiment = [self getExperimentForKey:experimentKey];
Expand Down
Loading