17
17
#import " OPTLYBaseCondition.h"
18
18
#import " OPTLYDatafileKeys.h"
19
19
#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
20
27
21
28
@implementation OPTLYBaseCondition
22
29
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
+
23
39
/* *
24
40
* 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
25
41
*/
@@ -32,77 +48,160 @@ + (OPTLYJSONKeyMapper*)keyMapper
32
48
}];
33
49
}
34
50
35
- + (BOOL ) isBaseConditionJSON : (NSData *)jsonData {
51
+ + (BOOL )isBaseConditionJSON : (nonnull NSData *)jsonData {
36
52
return [jsonData isKindOfClass: [NSDictionary class ]];
37
53
}
38
54
39
- -(nullable NSNumber *)evaluateMatchTypeExact : (NSDictionary <NSString *, NSObject *> *)attributes {
55
+ - (nullable NSNumber *)evaluateMatchTypeExact : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
40
56
// 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
41
69
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
+ }
43
77
44
78
if ([self .value isValidStringType ] && [userAttribute isValidStringType ]) {
45
- success = [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
79
+ return [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
46
80
}
47
81
else if ([self .value isValidNumericAttributeValue ] && [userAttribute isValidNumericAttributeValue ]) {
48
- success = [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
82
+ return [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
49
83
}
50
84
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]];
52
86
}
53
87
else if ([self .value isValidBooleanAttributeValue ] && [userAttribute isValidBooleanAttributeValue ]) {
54
- success = [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
88
+ return [NSNumber numberWithBool: [self .value isEqual: userAttribute]];
55
89
}
56
- return success ;
90
+ return NULL ;
57
91
}
58
92
59
- -(nullable NSNumber *)evaluateMatchTypeExist : (NSDictionary <NSString *, NSObject *> *)attributes {
93
+ - (nullable NSNumber *)evaluateMatchTypeExist : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
60
94
// check if user attributes contain our name as a key to a Non nullable object
61
95
return [NSNumber numberWithBool: ([attributes objectForKey: self .name] && ![attributes[self .name] isKindOfClass: [NSNull class ]])];
62
96
}
63
97
64
- -(nullable NSNumber *)evaluateMatchTypeSubstring : (NSDictionary <NSString *, NSObject *> *)attributes {
98
+ - (nullable NSNumber *)evaluateMatchTypeSubstring : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
65
99
// 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 ]]);
68
100
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 ;
72
104
}
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];
74
129
}
75
130
76
- -(nullable NSNumber *)evaluateMatchTypeGreaterThan : (NSDictionary <NSString *, NSObject *> *)attributes {
131
+ - (nullable NSNumber *)evaluateMatchTypeGreaterThan : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
77
132
// 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 ];
80
133
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 ;
85
137
}
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 ])];
87
163
}
88
164
89
- -(nullable NSNumber *)evaluateMatchTypeLessThan : (NSDictionary <NSString *, NSObject *> *)attributes {
165
+ - (nullable NSNumber *)evaluateMatchTypeLessThan : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
90
166
// 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 ];
93
167
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 ;
98
171
}
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 ])];
100
197
}
101
198
102
- -(nullable NSNumber *)evaluateCustomMatchType : (NSDictionary <NSString *, NSObject *> *)attributes {
199
+ - (nullable NSNumber *)evaluateCustomMatchType : (NSDictionary <NSString *, NSObject *> *)attributes projectConfig : (nullable OPTLYProjectConfig *) config {
103
200
104
201
if (![self .type isEqual: OPTLYDatafileKeysCustomAttributeConditionType]){
105
202
// Check if given type is the required type
203
+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorUnknownConditionType, self .stringRepresentation];
204
+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
106
205
return NULL ;
107
206
}
108
207
else if (self.value == NULL && ![self .match isEqualToString: OPTLYDatafileKeysMatchTypeExists]){
@@ -116,21 +215,23 @@ -(nullable NSNumber *)evaluateCustomMatchType:(NSDictionary<NSString *, NSObject
116
215
117
216
SWITCH (self.match ){
118
217
CASE (OPTLYDatafileKeysMatchTypeExact) {
119
- return [self evaluateMatchTypeExact: attributes];
218
+ return [self evaluateMatchTypeExact: attributes projectConfig: config ];
120
219
}
121
220
CASE (OPTLYDatafileKeysMatchTypeExists) {
122
- return [self evaluateMatchTypeExist: attributes];
221
+ return [self evaluateMatchTypeExist: attributes projectConfig: config ];
123
222
}
124
223
CASE (OPTLYDatafileKeysMatchTypeSubstring) {
125
- return [self evaluateMatchTypeSubstring: attributes];
224
+ return [self evaluateMatchTypeSubstring: attributes projectConfig: config ];
126
225
}
127
226
CASE (OPTLYDatafileKeysMatchTypeGreaterThan) {
128
- return [self evaluateMatchTypeGreaterThan: attributes];
227
+ return [self evaluateMatchTypeGreaterThan: attributes projectConfig: config ];
129
228
}
130
229
CASE (OPTLYDatafileKeysMatchTypeLessThan) {
131
- return [self evaluateMatchTypeLessThan: attributes];
230
+ return [self evaluateMatchTypeLessThan: attributes projectConfig: config ];
132
231
}
133
232
DEFAULT {
233
+ NSString *logMessage = [NSString stringWithFormat: OPTLYLoggerMessagesAudienceEvaluatorUnknownMatchType, self .stringRepresentation];
234
+ [config.logger logMessage: logMessage withLevel: OptimizelyLogLevelWarning];
134
235
return NULL ;
135
236
}
136
237
}
@@ -139,9 +240,9 @@ -(nullable NSNumber *)evaluateCustomMatchType:(NSDictionary<NSString *, NSObject
139
240
/* *
140
241
* Evaluates the condition against the user attributes, returns NULL if invalid.
141
242
*/
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 {
143
244
// 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 ];
145
246
}
146
247
147
248
@end
0 commit comments