1717#import " OPTLYBaseCondition.h"
1818#import " OPTLYDatafileKeys.h"
1919#import " OPTLYNSObject+Validation.h"
20+ #import " OPTLYLoggerMessages.h"
21+ #import " OPTLYLogger.h"
22+
23+ @interface OPTLYBaseCondition ()
24+ // / String representation of self
25+ @property (nonatomic , strong ) NSString <Ignore> *stringRepresentation;
26+ @end
2027
2128@implementation OPTLYBaseCondition
2229
30+ - (instancetype )initWithDictionary : (NSDictionary *)dict error : (NSError *__autoreleasing *)err {
31+ self.stringRepresentation = [NSString stringWithFormat: @" %@ " , dict];;
32+ return [super initWithDictionary: dict error: err];
33+ }
34+
35+ - (nonnull NSString *)toString {
36+ return _stringRepresentation ?: @" " ;
37+ }
38+
2339/* *
2440 * Given a json, this mapper finds JSON keys for each key in the provided dictionary and maps the json value to the class property with name corresponding to the dictionary value
2541 */
@@ -32,77 +48,160 @@ + (OPTLYJSONKeyMapper*)keyMapper
3248 }];
3349}
3450
35- + (BOOL ) isBaseConditionJSON : (NSData *)jsonData {
51+ + (BOOL )isBaseConditionJSON : (nonnull NSData *)jsonData {
3652 return [jsonData isKindOfClass: [NSDictionary class ]];
3753}
3854
39- -(nullable NSNumber *)evaluateMatchTypeExact : (NSDictionary <NSString *, NSObject *> *)attributes {
55+ - (nullable NSNumber *)evaluateMatchTypeExact : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
4056 // check if user attributes contain a value that is of similar class type to our value and also equals to our value, else return Null
57+
58+ // check if condition value is invalid
59+ if (![self .value isValidExactMatchTypeValue ]) {
60+ return NULL ;
61+ }
62+ // check if attributes exists
63+ if (![attributes.allKeys containsObject: self .name]) {
64+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForMissingAttribute, self .stringRepresentation, self .name];
65+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelDebug];
66+ return NULL ;
67+ }
68+ // check if attribute value is invalid
4169 NSObject *userAttribute = [attributes objectForKey: self .name];
42- NSNumber *success = NULL ;
70+ if (![userAttribute isValidExactMatchTypeValue ]) {
71+ // Log Invalid Attribute Value Type
72+ NSString *userAttributeClassName = NSStringFromClass ([userAttribute class ]) ?: @" null" ;
73+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForUnexpectedType, self .stringRepresentation, userAttributeClassName, self .name];
74+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
75+ return NULL ;
76+ }
4377
4478 if ([self .value isValidStringType ] && [userAttribute isValidStringType ]) {
45- success = [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
79+ return [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
4680 }
4781 else if ([self .value isValidNumericAttributeValue ] && [userAttribute isValidNumericAttributeValue ]) {
48- success = [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
82+ return [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
4983 }
5084 else if ([self .value isKindOfClass: [NSNull class ]] && [userAttribute isKindOfClass: [NSNull class ]]) {
51- success = [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
85+ return [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
5286 }
5387 else if ([self .value isValidBooleanAttributeValue ] && [userAttribute isValidBooleanAttributeValue ]) {
54- success = [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
88+ return [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
5589 }
56- return success ;
90+ return NULL ;
5791}
5892
59- -(nullable NSNumber *)evaluateMatchTypeExist : (NSDictionary <NSString *, NSObject *> *)attributes {
93+ - (nullable NSNumber *)evaluateMatchTypeExist : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
6094 // check if user attributes contain our name as a key to a Non nullable object
6195 return [NSNumber numberWithBool: ([attributes objectForKey: self .name] && ![attributes[self .name] isKindOfClass: [NSNull class ]])];
6296}
6397
64- -(nullable NSNumber *)evaluateMatchTypeSubstring : (NSDictionary <NSString *, NSObject *> *)attributes {
98+ - (nullable NSNumber *)evaluateMatchTypeSubstring : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
6599 // check if user attributes contain our value as substring
66- NSObject *userAttribute = [attributes objectForKey: self .name];
67- BOOL userAndOurValueHaveStringClassTypes = ([self .value isKindOfClass: [NSString class ]] && [userAttribute isKindOfClass: [NSString class ]]);
68100
69- if (userAndOurValueHaveStringClassTypes) {
70- BOOL containsSubstring = [(( NSString *)userAttribute) containsString: ( NSString *) self .value];
71- return [ NSNumber numberWithBool: containsSubstring] ;
101+ // check if condition value is invalid
102+ if (self. value == nil || [ self .value isKindOfClass: [ NSNull class ]] || ![ self .value isKindOfClass: [ NSString class ]]) {
103+ return NULL ;
72104 }
73- return NULL ;
105+ // check if attributes exists
106+ if (![attributes.allKeys containsObject: self .name]) {
107+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForMissingAttribute, self .stringRepresentation, self .name];
108+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelDebug];
109+ return NULL ;
110+ }
111+ // check if user attributes are invalid
112+ NSObject *userAttribute = [attributes objectForKey: self .name];
113+ if (![userAttribute isKindOfClass: [NSString class ]]) {
114+ // Log Invalid Attribute Value Type
115+ if (!userAttribute || [userAttribute isKindOfClass: [NSNull class ]]) {
116+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForUnexpectedTypeNull, self .stringRepresentation, self .name];
117+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
118+ }
119+ else {
120+ NSString *userAttributeClassName = NSStringFromClass ([userAttribute class ]);
121+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForUnexpectedType, self .stringRepresentation, userAttributeClassName, self .name];
122+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
123+ }
124+ return NULL ;
125+ }
126+
127+ BOOL containsSubstring = [((NSString *)userAttribute) containsString: (NSString *)self .value];
128+ return [NSNumber numberWithBool: containsSubstring];
74129}
75130
76- -(nullable NSNumber *)evaluateMatchTypeGreaterThan : (NSDictionary <NSString *, NSObject *> *)attributes {
131+ - (nullable NSNumber *)evaluateMatchTypeGreaterThan : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
77132 // check if user attributes contain a value greater than our value
78- NSObject *userAttribute = [attributes objectForKey: self .name];
79- BOOL userValueAndOurValueHaveNSNumberClassTypes = [self .value isValidNumericAttributeValue ] && [userAttribute isValidNumericAttributeValue ];
80133
81- if (userValueAndOurValueHaveNSNumberClassTypes) {
82- NSNumber *ourValue = (NSNumber *)self.value ;
83- NSNumber *userValue = (NSNumber *)userAttribute;
84- return [NSNumber numberWithBool: ([userValue doubleValue ] > [ourValue doubleValue ])];
134+ // check if condition value is invalid
135+ if (self.value == nil || [self .value isKindOfClass: [NSNull class ]] || ![self .value isValidNumericAttributeValue ]) {
136+ return NULL ;
85137 }
86- return NULL ;
138+ // check if attributes exists
139+ if (![attributes.allKeys containsObject: self .name]) {
140+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForMissingAttribute, self .stringRepresentation, self .name];
141+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelDebug];
142+ return NULL ;
143+ }
144+ // check if user attributes are invalid
145+ NSObject *userAttribute = [attributes objectForKey: self .name];
146+ if (![userAttribute isValidNumericAttributeValue ]) {
147+ // Log Invalid Attribute Value Type
148+ if (!userAttribute || [userAttribute isKindOfClass: [NSNull class ]]) {
149+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForUnexpectedTypeNull, self .stringRepresentation, self .name];
150+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
151+ }
152+ else {
153+ NSString *userAttributeClassName = NSStringFromClass ([userAttribute class ]);
154+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForUnexpectedType, self .stringRepresentation, userAttributeClassName, self .name];
155+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
156+ }
157+ return NULL ;
158+ }
159+
160+ NSNumber *ourValue = (NSNumber *)self.value ;
161+ NSNumber *userValue = (NSNumber *)userAttribute;
162+ return [NSNumber numberWithBool: ([userValue doubleValue ] > [ourValue doubleValue ])];
87163}
88164
89- -(nullable NSNumber *)evaluateMatchTypeLessThan : (NSDictionary <NSString *, NSObject *> *)attributes {
165+ - (nullable NSNumber *)evaluateMatchTypeLessThan : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
90166 // check if user attributes contain a value lesser than our value
91- NSObject *userAttribute = [attributes objectForKey: self .name];
92- BOOL userValueAndOurValueHaveNSNumberClassTypes = [self .value isValidNumericAttributeValue ] && [userAttribute isValidNumericAttributeValue ];
93167
94- if (userValueAndOurValueHaveNSNumberClassTypes) {
95- NSNumber *ourValue = (NSNumber *)self.value ;
96- NSNumber *userValue = (NSNumber *)userAttribute;
97- return [NSNumber numberWithBool: ([userValue doubleValue ] < [ourValue doubleValue ])];
168+ // check if condition value is invalid
169+ if (![self .value isValidGTLTMatchTypeValue ]) {
170+ return NULL ;
98171 }
99- return NULL ;
172+ // check if attributes exists
173+ if (![attributes.allKeys containsObject: self .name]) {
174+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForMissingAttribute, self .stringRepresentation, self .name];
175+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelDebug];
176+ return NULL ;
177+ }
178+ // check if user attributes are invalid
179+ NSObject *userAttribute = [attributes objectForKey: self .name];
180+ if (![userAttribute isValidNumericAttributeValue ]) {
181+ // Log Invalid Attribute Value Type
182+ if (!userAttribute || [userAttribute isKindOfClass: [NSNull class ]]) {
183+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForUnexpectedTypeNull, self .stringRepresentation, self .name];
184+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
185+ }
186+ else {
187+ NSString *userAttributeClassName = NSStringFromClass ([userAttribute class ]);
188+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorConditionEvaluatedAsUnknownForUnexpectedType, self .stringRepresentation, userAttributeClassName, self .name];
189+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
190+ }
191+ return NULL ;
192+ }
193+
194+ NSNumber *ourValue = (NSNumber *)self.value ;
195+ NSNumber *userValue = (NSNumber *)userAttribute;
196+ return [NSNumber numberWithBool: ([userValue doubleValue ] < [ourValue doubleValue ])];
100197}
101198
102- -(nullable NSNumber *)evaluateCustomMatchType : (NSDictionary <NSString *, NSObject *> *)attributes {
199+ - (nullable NSNumber *)evaluateCustomMatchType : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
103200
104201 if (![self .type isEqual: OPTLYDatafileKeysCustomAttributeConditionType]){
105202 // Check if given type is the required type
203+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorUnknownConditionType, self .stringRepresentation];
204+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
106205 return NULL ;
107206 }
108207 else if (self.value == NULL && ![self .match isEqualToString: OPTLYDatafileKeysMatchTypeExists]){
@@ -116,21 +215,23 @@ -(nullable NSNumber *)evaluateCustomMatchType:(NSDictionary<NSString *, NSObject
116215
117216 SWITCH (self.match ){
118217 CASE (OPTLYDatafileKeysMatchTypeExact) {
119- return [self evaluateMatchTypeExact: attributes];
218+ return [self evaluateMatchTypeExact: attributes projectConfig: config ];
120219 }
121220 CASE (OPTLYDatafileKeysMatchTypeExists) {
122- return [self evaluateMatchTypeExist: attributes];
221+ return [self evaluateMatchTypeExist: attributes projectConfig: config ];
123222 }
124223 CASE (OPTLYDatafileKeysMatchTypeSubstring) {
125- return [self evaluateMatchTypeSubstring: attributes];
224+ return [self evaluateMatchTypeSubstring: attributes projectConfig: config ];
126225 }
127226 CASE (OPTLYDatafileKeysMatchTypeGreaterThan) {
128- return [self evaluateMatchTypeGreaterThan: attributes];
227+ return [self evaluateMatchTypeGreaterThan: attributes projectConfig: config ];
129228 }
130229 CASE (OPTLYDatafileKeysMatchTypeLessThan) {
131- return [self evaluateMatchTypeLessThan: attributes];
230+ return [self evaluateMatchTypeLessThan: attributes projectConfig: config ];
132231 }
133232 DEFAULT {
233+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorUnknownMatchType, self .stringRepresentation];
234+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
134235 return NULL ;
135236 }
136237 }
@@ -139,9 +240,9 @@ -(nullable NSNumber *)evaluateCustomMatchType:(NSDictionary<NSString *, NSObject
139240/* *
140241 * Evaluates the condition against the user attributes, returns NULL if invalid.
141242 */
142- - (nullable NSNumber *)evaluateConditionsWithAttributes : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *)config {
243+ - (nullable NSNumber *)evaluateConditionsWithAttributes : (nullable NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *)config {
143244 // check user attribute value for the condition and match type against our condition value
144- return [self evaluateCustomMatchType: attributes];
245+ return [self evaluateCustomMatchType: attributes projectConfig: config ];
145246}
146247
147248@end
0 commit comments