Skip to content

Commit bc0dc86

Browse files
Audience Evaluation Logging. (#362)
* Audience Logging Implementations. * cleaning unused code. * 1. Fixed code review recommendations. 2. Fixed indentation of method declarations throughout the SDK. * Fixed all condition warnings for Scripts/build_all.sh. (#369)
1 parent 3007efc commit bc0dc86

31 files changed

+662
-158
lines changed

OptimizelySDKCore/OptimizelySDKCore/OPTLYAudience.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,7 @@
3939
- (void)setConditionsWithNSArray:(NSArray *)array;
4040
- (void)setConditionsWithNSDictionary:(NSDictionary *)dictionary;
4141

42+
/// Returns conditions string
43+
- (NSString *)getConditionsString;
44+
4245
@end

OptimizelySDKCore/OptimizelySDKCore/OPTLYAudience.m

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,17 @@
1414
* limitations under the License. *
1515
***************************************************************************/
1616

17+
1718
#import "OPTLYAudience.h"
1819
#import "OPTLYDatafileKeys.h"
1920
#import "OPTLYNSObject+Validation.h"
21+
#import "OPTLYLoggerMessages.h"
22+
#import "OPTLYLogger.h"
23+
24+
@interface OPTLYAudience()
25+
/// String representation of the conditions
26+
@property (nonatomic, strong) NSString<Ignore> *conditionsString;
27+
@end
2028

2129
@implementation OPTLYAudience
2230

@@ -27,12 +35,18 @@ + (OPTLYJSONKeyMapper*)keyMapper
2735
}];
2836
}
2937

38+
- (NSString *)getConditionsString {
39+
return _conditionsString ?: @"";
40+
}
41+
3042
- (void)setConditionsWithNSString:(NSString *)string {
3143
NSArray *array = [string getValidConditionsArray];
3244
[self setConditionsWithNSArray:array];
3345
}
3446

3547
- (void)setConditionsWithNSArray:(NSArray *)array {
48+
//Retrieving Jsonstring from array
49+
_conditionsString = [array getJSONArrayStringOrEmpty];
3650
NSError *err = nil;
3751
self.conditions = [OPTLYCondition deserializeJSON:array error:nil];
3852
if (err != nil) {
@@ -50,11 +64,24 @@ - (void)setConditionsWithNSDictionary:(NSDictionary *)dictionary {
5064
}
5165
}
5266

53-
- (nullable NSNumber *)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSObject *> *)attributes projectConfig:(nullable OPTLYProjectConfig *)config {
54-
67+
- (nullable NSNumber *)evaluateConditionsWithAttributes:(nullable NSDictionary<NSString *, NSObject *> *)attributes projectConfig:(nullable OPTLYProjectConfig *)config {
5568
NSObject<OPTLYCondition> *condition = (NSObject<OPTLYCondition> *)[self.conditions firstObject];
5669
if (condition) {
57-
return [condition evaluateConditionsWithAttributes:attributes projectConfig:config];
70+
// Log Audience Evaluation Started
71+
NSString *conditionString = [self getConditionsString];
72+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAudienceEvaluatorEvaluationStartedWithConditions, self.audienceName, conditionString];
73+
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
74+
75+
NSNumber *result = [condition evaluateConditionsWithAttributes:attributes projectConfig:config];
76+
if (result == NULL) {
77+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAudienceEvaluatorEvaluationCompletedWithResult, self.audienceName, @"UNKNOWN"];
78+
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelInfo];
79+
}
80+
else {
81+
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesAudienceEvaluatorEvaluationCompletedWithResult, self.audienceName, [result boolValue] ? @"TRUE" : @"FALSE"];
82+
[config.logger logMessage:logMessage withLevel:OptimizelyLogLevelInfo];
83+
}
84+
return result;
5885
}
5986
return nil;
6087
}

OptimizelySDKCore/OptimizelySDKCore/OPTLYAudienceBaseCondition.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626

2727
@interface OPTLYAudienceBaseCondition : NSObject <OPTLYCondition>
2828

29-
@property (nonatomic, strong) NSString *audienceId;
30-
+(BOOL)isBaseConditionJSON:(NSData *)jsonData;
29+
@property (nonatomic, strong, nonnull) NSString *audienceId;
30+
+(BOOL)isBaseConditionJSON:(nonnull NSData *)jsonData;
3131

3232
@end
3333

OptimizelySDKCore/OptimizelySDKCore/OPTLYAudienceBaseCondition.m

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,25 @@
1616

1717
#import "OPTLYAudienceBaseCondition.h"
1818
#import "OPTLYAudience.h"
19+
#import "OPTLYLogger.h"
20+
#import "OPTLYLoggerMessages.h"
21+
#import "OPTLYNSObject+Validation.h"
1922

2023
@implementation OPTLYAudienceBaseCondition
2124

22-
+ (BOOL) isBaseConditionJSON:(NSData *)jsonData {
25+
+ (BOOL) isBaseConditionJSON:(nonnull NSData *)jsonData {
2326
return [jsonData isKindOfClass:[NSString class]];
2427
}
2528

26-
- (nullable NSNumber *)evaluateConditionsWithAttributes:(NSDictionary<NSString *, NSObject *> *)attributes projectConfig:(nullable OPTLYProjectConfig *)config {
29+
- (nullable NSNumber *)evaluateConditionsWithAttributes:(nullable NSDictionary<NSString *, NSObject *> *)attributes projectConfig:(nullable OPTLYProjectConfig *)config {
2730
if (attributes == nil) {
2831
// if the user did not pass in attributes, return false
2932
return [NSNumber numberWithBool:false];
3033
}
31-
3234
OPTLYAudience *audience = [config getAudienceForId:self.audienceId];
35+
if (audience == nil) {
36+
return nil;
37+
}
3338
return [audience evaluateConditionsWithAttributes:attributes projectConfig:config];
3439
}
3540

OptimizelySDKCore/OptimizelySDKCore/OPTLYBaseCondition.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,17 @@
3232
@interface OPTLYBaseCondition : OPTLYJSONModel <OPTLYCondition>
3333

3434
/// Condition name
35-
@property (nonatomic, strong) NSString *name;
35+
@property (nonatomic, strong, nonnull) NSString *name;
3636
/// Condition type
3737
@property (nonatomic, strong, nullable) NSString<OPTLYOptional> *type;
3838
/// Condition value
3939
@property (nonatomic, strong, nullable) NSObject<OPTLYOptional> *value;
4040
/// Condition match type
4141
@property (nonatomic, strong, nullable) NSString<OPTLYOptional> *match;
4242

43+
/// Returns string representation
44+
- (nonnull NSString *)toString;
4345

44-
+(BOOL)isBaseConditionJSON:(NSData *)jsonData;
46+
+(BOOL)isBaseConditionJSON:(nonnull NSData *)jsonData;
4547

4648
@end

OptimizelySDKCore/OptimizelySDKCore/OPTLYBaseCondition.m

Lines changed: 141 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,25 @@
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

Comments
 (0)