Skip to content

Commit e8b5666

Browse files
example to show folio3 how to refactor code (#238)
* example to show folio3 how to refactor code * write failing test * fix single quote test * get record id to attach with event to remove * fix for TVOS events removal
1 parent 757bc8c commit e8b5666

File tree

10 files changed

+192
-31
lines changed

10 files changed

+192
-31
lines changed

OptimizelySDKCore/OptimizelySDKCore/OPTLYQueue.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ extern const NSInteger OPTLYQueueDefaultMaxSize;
7474
*/
7575
- (id)front;
7676

77+
/**
78+
* Returns an index of the latest item in the queue (the queue is not mutated).
79+
*
80+
* @return An index of the latest item in the queue.
81+
*/
82+
- (NSInteger)lastItemIndex;
83+
7784
/**
7885
* Returns a copy of the oldest N items in the queue (the queue is not mutated).
7986
*

OptimizelySDKCore/OptimizelySDKCore/OPTLYQueue.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ - (id)front {
6060
return item;
6161
}
6262

63+
- (NSInteger)lastItemIndex {
64+
return [self.mutableQueue count]-1;
65+
}
66+
6367
- (NSArray *)firstNItems:(NSInteger)numberOfItems {
6468
NSArray *items;
6569
if (!self.isEmpty) {

OptimizelySDKEventDispatcher/OptimizelySDKEventDispatcher/OPTLYEventDispatcher.m

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,12 +187,19 @@ - (void)dispatchNewEvent:(nonnull NSDictionary *)params
187187
NSString *eventName = [OPTLYDataStore stringForDataEventEnum:eventType];
188188
NSError *saveError = nil;
189189
[self.dataStore saveEvent:params eventType:eventType error:&saveError];
190+
NSDictionary *savedEvent = params;
190191
if (!saveError) {
191192
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesEventDispatcherEventSaved, eventName, params];
192193
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
194+
NSError *getError = nil;
195+
NSInteger entityId = [self.dataStore getLastEventId:eventType error:&getError];
196+
if (!getError) {
197+
NSDictionary *event = @{ @"entityId": @(entityId), @"json": params };
198+
savedEvent = event == nil ? savedEvent : event;
199+
}
193200
}
194201

195-
[self dispatchEvent:params backoffRetry:backoffRetry eventType:eventType callback:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
202+
[self dispatchEvent:savedEvent backoffRetry:backoffRetry eventType:eventType callback:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
196203
[self flushEvents];
197204
if (callback) {
198205
callback(data, response, error);
@@ -223,8 +230,9 @@ - (void)dispatchEvent:(nonnull NSDictionary *)event
223230

224231
NSURL *url = [self URLForEvent:eventType];
225232

233+
NSDictionary *eventToSend = event[@"json"] == nil ? event : event[@"json"];
226234
__weak typeof(self) weakSelf = self;
227-
[self.networkService dispatchEvent:event
235+
[self.networkService dispatchEvent:eventToSend
228236
backoffRetry:backoffRetry
229237
toURL:url
230238
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

OptimizelySDKShared/OptimizelySDKShared/OPTLYDataStore.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ typedef NS_ENUM(NSUInteger, OPTLYDataStoreEventType)
196196
eventType:(OPTLYDataStoreEventType)eventType
197197
error:(NSError * _Nullable __autoreleasing * _Nullable)error;
198198

199+
/**
200+
* Gets the last entry id.
201+
*
202+
* @param eventType The event type of the data that needs to be removed.
203+
* @param error An error object is returned if an error occurs.
204+
*/
205+
- (NSInteger)getLastEventId:(OPTLYDataStoreEventType)eventType
206+
error:(NSError * _Nullable __autoreleasing * _Nullable)error;
207+
199208
/**
200209
* Gets all events.
201210
*

OptimizelySDKShared/OptimizelySDKShared/OPTLYDataStore.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,15 @@ - (nullable NSDictionary *)getOldestEvent:(OPTLYDataStoreEventType)eventType
310310
return oldestEvent;
311311
}
312312

313+
- (NSInteger)getLastEventId:(OPTLYDataStoreEventType)eventType
314+
error:(NSError * _Nullable __autoreleasing * _Nullable)error
315+
{
316+
NSString *eventTypeName = [OPTLYDataStore stringForDataEventEnum:eventType];
317+
NSInteger lastRowId = [self.eventDataStore getLastEventId:eventTypeName error:error];
318+
return lastRowId;
319+
}
320+
321+
313322
- (nullable NSArray *)getAllEvents:(OPTLYDataStoreEventType)eventType
314323
error:(NSError * _Nullable __autoreleasing * _Nullable)error
315324
{

OptimizelySDKShared/OptimizelySDKShared/OPTLYDatabase.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@
109109
table:(nonnull NSString *)tableName
110110
error:(NSError * _Nullable __autoreleasing * _Nullable)error;
111111

112+
/**
113+
* Retrieves an id of last entry from the table.
114+
*
115+
* @param tableName The database table name.
116+
* @param error An error object is returned if an error occurs.
117+
* @return The return value is an id of NSInteger
118+
*/
119+
120+
- (NSInteger)retrieveLastEntryId:(NSString * _Nonnull)tableName
121+
error:(NSError * _Nullable __autoreleasing * _Nullable)error;
122+
112123
/**
113124
* Returns the number of rows of a table.
114125
*

OptimizelySDKShared/OptimizelySDKShared/OPTLYDatabase.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
static NSString * const kDeleteEntityIDQuery = @"DELETE FROM %@ where id IN %@";
3535
static NSString * const kDeleteEntityQuery = @"DELETE FROM %@ where json='%@'";
3636
static NSString * const kRetrieveEntityQuery = @"SELECT * from %@";
37+
static NSString * const kRetrieveLastEntityIdQuery = @"select last_insert_rowid()";
3738
static NSString * const kRetrieveEntityQueryLimit = @" LIMIT %ld";
3839
static NSString * const kEntitiesCountQuery = @"SELECT count(*) FROM %@";
3940

@@ -228,6 +229,33 @@ - (NSArray *)retrieveFirstNEntries:(NSInteger)numberOfEntries
228229
return results;
229230
}
230231

232+
- (NSInteger)retrieveLastEntryId:(NSString *)tableName
233+
error:(NSError * __autoreleasing *)error
234+
{
235+
__block NSInteger rowId;
236+
[self.fmDatabaseQueue inDatabase:^(OPTLYFMDBDatabase *db){
237+
NSMutableString *query = [NSMutableString stringWithFormat:kRetrieveLastEntityIdQuery];
238+
239+
OPTLYFMDBResultSet *resultSet = [db executeQuery:query];
240+
if (!resultSet) {
241+
if (error) {
242+
*error = [NSError errorWithDomain:OPTLYErrorHandlerMessagesDomain
243+
code:OPTLYErrorTypesDatabase
244+
userInfo:@{NSLocalizedDescriptionKey :
245+
NSLocalizedString([db lastErrorMessage], nil)}];
246+
}
247+
OPTLYLogError(@"Unable to retrieve rows of Optimizely table: %@ %@", tableName, [db lastErrorMessage]);
248+
}
249+
250+
if ([resultSet next]) {
251+
rowId = [resultSet intForColumnIndex:0];
252+
}
253+
[resultSet close];
254+
}];
255+
256+
return rowId;
257+
}
258+
231259
- (NSInteger)numberOfRows:(NSString *)tableName
232260
error:(NSError * __autoreleasing *)error
233261
{

OptimizelySDKShared/OptimizelySDKShared/OPTLYEventDataStore.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@
5454
eventType:(nonnull NSString *)eventTypeName
5555
error:(NSError * _Nullable __autoreleasing * _Nullable)error;
5656

57+
/**
58+
* Gets the last entry id.
59+
*
60+
* @param eventTypeName The name of the event type of the data that needs to be removed.
61+
* @param error An error object is returned if an error occurs.
62+
*/
63+
- (NSInteger)getLastEventId:(nonnull NSString *)eventTypeName
64+
error:(NSError * _Nullable __autoreleasing * _Nullable)error;
65+
5766
/**
5867
* Deletes the first N events (i.e., the N oldest events).
5968
*

OptimizelySDKShared/OptimizelySDKShared/OPTLYEventDataStore.m

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,22 @@ - (nullable NSArray *)getFirstNEvents:(NSInteger)numberOfEvents
7676
NSDictionary *event = [NSJSONSerialization JSONObjectWithData:[entityValue dataUsingEncoding:NSUTF8StringEncoding] options:0 error:error];
7777

7878
if ([event count] > 0) {
79-
if (error != nil) {
80-
if (*error == nil) {
81-
[firstNEvents addObject:event];
82-
}
83-
} else {
84-
[firstNEvents addObject:event];
79+
if ((error != nil && *error == nil) || error == nil) {
80+
[firstNEvents addObject:@{@"entityId": entity.entityId, @"json": event}];
8581
}
8682
}
8783
}
8884

8985
return [firstNEvents copy];
9086
}
9187

88+
- (NSInteger)getLastEventId:(nonnull NSString *)eventTypeName
89+
error:(NSError * _Nullable __autoreleasing * _Nullable)error
90+
{
91+
NSInteger lastRowId = [self.database retrieveLastEntryId:eventTypeName error:error];
92+
return lastRowId;
93+
}
94+
9295
- (BOOL)removeFirstNEvents:(NSInteger)numberOfEvents
9396
eventType:(nonnull NSString *)eventTypeName
9497
error:(NSError * _Nullable __autoreleasing * _Nullable)error
@@ -131,9 +134,12 @@ - (BOOL)removeEvent:(nonnull NSDictionary *)event
131134
eventType:(nonnull NSString *)eventTypeName
132135
error:(NSError * _Nullable __autoreleasing * _Nullable)error
133136
{
134-
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:event options:NSJSONWritingPrettyPrinted error:error];
135-
NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
136-
return [self.database deleteEntityWithJSON:json table:eventTypeName error:error];
137+
BOOL retval = false;
138+
139+
if (event[@"entityId"] != nil) {
140+
retval = [self.database deleteEntity:event[@"entityId"] table:eventTypeName error:error];
141+
}
142+
return retval;
137143
}
138144

139145
- (NSInteger)numberOfEvents:(NSString *)eventTypeName
@@ -206,10 +212,28 @@ - (nullable NSArray *)getFirstNEvents:(NSInteger)numberOfEvents
206212
queue = [weakSelf.eventsCache objectForKey:eventTypeName];
207213
});
208214

209-
NSArray *firstNEvents = [queue firstNItems:numberOfEvents];
215+
NSArray *firstNEntities = [queue firstNItems:numberOfEvents];
216+
NSMutableArray *firstNEvents = [NSMutableArray new];
217+
for (NSDictionary *entity in firstNEntities) {
218+
if ([entity count] > 0) {
219+
[firstNEvents addObject:@{@"entityId": @([firstNEntities indexOfObject:entity]) , @"json": entity}];
220+
}
221+
}
210222
return firstNEvents;
211223
}
212224

225+
- (NSInteger)getLastEventId:(nonnull NSString *)eventTypeName
226+
error:(NSError * _Nullable __autoreleasing * _Nullable)error
227+
{
228+
__block OPTLYQueue *queue = nil;
229+
dispatch_sync(eventsStorageCacheQueue(), ^{
230+
__weak typeof(self) weakSelf = self;
231+
queue = [weakSelf.eventsCache objectForKey:eventTypeName];
232+
});
233+
234+
return [queue lastItemIndex];
235+
}
236+
213237
- (BOOL)removeFirstNEvents:(NSInteger)numberOfEvents
214238
eventType:(nonnull NSString *)eventTypeName
215239
error:(NSError * _Nullable __autoreleasing * _Nullable)error
@@ -226,12 +250,18 @@ - (BOOL)removeEvent:(nonnull NSDictionary *)event
226250
eventType:(nonnull NSString *)eventTypeName
227251
error:(NSError * _Nullable __autoreleasing * _Nullable)error
228252
{
229-
dispatch_async(eventsStorageCacheQueue(), ^{
230-
__weak typeof(self) weakSelf = self;
231-
OPTLYQueue *queue = [weakSelf.eventsCache objectForKey:eventTypeName];
232-
[queue removeItem:event];
233-
});
234-
return YES;
253+
BOOL retval = NO;
254+
255+
if (event[@"entityId"] != nil) {
256+
dispatch_async(eventsStorageCacheQueue(), ^{
257+
__weak typeof(self) weakSelf = self;
258+
OPTLYQueue *queue = [weakSelf.eventsCache objectForKey:eventTypeName];
259+
NSDictionary *eventJSON = [queue.queue objectAtIndex:[event[@"entityId"] integerValue]];
260+
[queue removeItem:eventJSON];
261+
});
262+
retval = YES;
263+
}
264+
return retval;
235265
}
236266

237267
- (NSInteger)numberOfEvents:(nonnull NSString *)eventTypeName

OptimizelySDKShared/OptimizelySDKSharedTests/OPTLYDataStoreTest.m

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -203,23 +203,23 @@ - (void)testEventsStorage {
203203
// ---- test getOldestEvent ----
204204
NSDictionary *result = [self.dataStore getOldestEvent:OPTLYDataStoreEventTypeImpression error:&error];
205205
XCTAssertNotNil(result, @"Data insertion failed or invalid number of results retrieved from getOldestEvent.");
206-
XCTAssert([result isEqualToDictionary:testEventData1], @"Invalid result data retrieved for getOldestEvent.");
206+
XCTAssert([result[@"json"] isEqualToDictionary:testEventData1], @"Invalid result data retrieved for getOldestEvent.");
207207

208208
// ---- test getFirstNEvents ----
209209
NSInteger n = 3;
210210
results = [self.dataStore getFirstNEvents:n eventType:OPTLYDataStoreEventTypeImpression error:&error];
211211
XCTAssert([results count] == n, @"Data insertion failed or invalid number of results retrieved from getFirstNEvents.");
212-
XCTAssert([results[0] isEqualToDictionary:testEventData1], @"Invalid result data 1 retrieved for getFirstNEvents.");
213-
XCTAssert([results[1] isEqualToDictionary:testEventData2], @"Invalid result data 2 retrieved for getFirstNEvents.");
214-
XCTAssert([results[2] isEqualToDictionary:testEventData3], @"Invalid result data 3 retrieved for getFirstNEvents.");
212+
XCTAssert([results[0][@"json"] isEqualToDictionary:testEventData1], @"Invalid result data 1 retrieved for getFirstNEvents.");
213+
XCTAssert([results[1][@"json"] isEqualToDictionary:testEventData2], @"Invalid result data 2 retrieved for getFirstNEvents.");
214+
XCTAssert([results[2][@"json"] isEqualToDictionary:testEventData3], @"Invalid result data 3 retrieved for getFirstNEvents.");
215215

216216
// ---- test getAllEntries ----
217217
results = [self.dataStore getAllEvents:OPTLYDataStoreEventTypeImpression error:&error];
218218
XCTAssert([results count] == totalEntity, @"Data insertion failed or invalid number of results retrieved from getAllEvents");
219-
XCTAssert([results[0] isEqualToDictionary:testEventData1], @"Invalid result data 1 retrieved for getAllEvents.");
220-
XCTAssert([results[1] isEqualToDictionary:testEventData2], @"Invalid result data 2 retrieved for getAllEvents.");
221-
XCTAssert([results[2] isEqualToDictionary:testEventData3], @"Invalid result data 3 retrieved for getAllEvents.");
222-
XCTAssert([results[3] isEqualToDictionary:testEventData4], @"Invalid result data 4 retrieved for getAllEvents.");
219+
XCTAssert([results[0][@"json"] isEqualToDictionary:testEventData1], @"Invalid result data 1 retrieved for getAllEvents.");
220+
XCTAssert([results[1][@"json"] isEqualToDictionary:testEventData2], @"Invalid result data 2 retrieved for getAllEvents.");
221+
XCTAssert([results[2][@"json"] isEqualToDictionary:testEventData3], @"Invalid result data 3 retrieved for getAllEvents.");
222+
XCTAssert([results[3][@"json"] isEqualToDictionary:testEventData4], @"Invalid result data 4 retrieved for getAllEvents.");
223223

224224
// ---- test numberOfEvents ----
225225
NSInteger numberOfEvents = [self.dataStore numberOfEvents:OPTLYDataStoreEventTypeImpression error:&error];
@@ -230,16 +230,16 @@ - (void)testEventsStorage {
230230
results = [self.dataStore getAllEvents:OPTLYDataStoreEventTypeImpression error:&error];
231231
numberOfEvents = [self.dataStore numberOfEvents:OPTLYDataStoreEventTypeImpression error:&error];
232232
XCTAssert(numberOfEvents == totalEntity-1, @"Invalid event count after removeOldestEvent was called.");
233-
XCTAssert([results[0] isEqualToDictionary:testEventData2], @"Invalid result data 1 retrieved after removeOldestEvent was called.");
234-
XCTAssert([results[1] isEqualToDictionary:testEventData3], @"Invalid result data 2 retrieved after removeOldestEvent was called.");
235-
XCTAssert([results[2] isEqualToDictionary:testEventData4], @"Invalid result data 3 retrieved after removeOldestEvent was called.");
233+
XCTAssert([results[0][@"json"] isEqualToDictionary:testEventData2], @"Invalid result data 1 retrieved after removeOldestEvent was called.");
234+
XCTAssert([results[1][@"json"] isEqualToDictionary:testEventData3], @"Invalid result data 2 retrieved after removeOldestEvent was called.");
235+
XCTAssert([results[2][@"json"] isEqualToDictionary:testEventData4], @"Invalid result data 3 retrieved after removeOldestEvent was called.");
236236

237237
// ---- test removeFirstNEvents ----
238238
NSInteger nEventsToDelete = 2;
239239
[self.dataStore removeFirstNEvents:nEventsToDelete eventType:OPTLYDataStoreEventTypeImpression error:&error];
240240
results = [self.dataStore getAllEvents:OPTLYDataStoreEventTypeImpression error:&error];
241241
XCTAssert([results count] == totalEntity-1-nEventsToDelete, @"Invalid event count when removeFirstNEvents was called.");
242-
XCTAssert([results[0] isEqualToDictionary:testEventData4], @"Invalid result data 1 retrieved after removeFirstNEvents was called.");
242+
XCTAssert([results[0][@"json"] isEqualToDictionary:testEventData4], @"Invalid result data 1 retrieved after removeFirstNEvents was called.");
243243

244244
// ---- test removeAllEvents of an event type ----
245245
[self.dataStore removeAllEvents:OPTLYDataStoreEventTypeImpression error:&error];
@@ -248,7 +248,8 @@ - (void)testEventsStorage {
248248

249249
// ---- test removeEvent ----
250250
[self.dataStore saveEvent:testEventData1 eventType:OPTLYDataStoreEventTypeImpression error:&error];
251-
[self.dataStore removeEvent:testEventData1 eventType:OPTLYDataStoreEventTypeImpression error:&error];
251+
NSDictionary *event = [self.dataStore getOldestEvent:OPTLYDataStoreEventTypeImpression error:&error];
252+
[self.dataStore removeEvent:event eventType:OPTLYDataStoreEventTypeImpression error:&error];
252253
results = [self.dataStore getAllEvents:OPTLYDataStoreEventTypeImpression error:&error];
253254
XCTAssert([results count] == 0, @"Invalid impression event count when removeEvent was called.");
254255

@@ -262,6 +263,51 @@ - (void)testEventsStorage {
262263
XCTAssert([results count] == 0, @"Invalid conversion event count when removeSavedEvents was called.");
263264
}
264265

266+
- (void)testSingleQuoteStringSaveAndRemove {
267+
NSDictionary *testEventData1 =
268+
@{
269+
@"userFeatures": @[@{
270+
@"value": @"ali'`s",
271+
@"shouldIndex": @YES,
272+
@"name": @"nameOfPerson",
273+
@"type": @"custom"
274+
}],
275+
@"timestamp": @1478510071576,
276+
@"clientVersion": @"0.2.0-debug",
277+
@"eventEntityId": @"7723870635",
278+
@"revision": @"7",
279+
@"isGlobalHoldback": @NO,
280+
@"accountId": @"4902200114",
281+
@"layerStates": @[],
282+
@"projectId": @"7738070017",
283+
@"eventMetrics": @[@{
284+
@"name": @"revenue",
285+
@"value": @88
286+
}],
287+
@"visitorId": @"1",
288+
@"eventName": @"people",
289+
@"clientEngine": kClientEngine,
290+
@"eventFeatures": @[]
291+
};
292+
293+
NSError *error = nil;
294+
295+
XCTAssertTrue([self.dataStore saveEvent:testEventData1
296+
eventType:OPTLYDataStoreEventTypeConversion
297+
error:&error]);
298+
XCTAssertNil(error);
299+
NSArray *events = [self.dataStore getAllEvents:OPTLYDataStoreEventTypeConversion error:&error];
300+
301+
XCTAssertNil(error);
302+
303+
NSDictionary *event = [events lastObject][@"json"];
304+
XCTAssertTrue([event isEqual:testEventData1]);
305+
XCTAssertTrue([self.dataStore removeEvent:[events lastObject]
306+
eventType:OPTLYDataStoreEventTypeConversion
307+
error:&error]);
308+
XCTAssertNil(error);
309+
}
310+
265311
# pragma mark - File Manager Tests
266312

267313
- (void)testSaveFile {

0 commit comments

Comments
 (0)