Skip to content

Commit

Permalink
Peek and Remove n for DataQueue (#453)
Browse files Browse the repository at this point in the history
* Add ability to peek and remove n in the DataQueueService

* Replace pop with remove in test funciton name

* Add bound check for n in remove

* Uppercase SQL statement
  • Loading branch information
nporter-adbe authored Nov 20, 2020
1 parent e7cf163 commit 8ace745
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 28 deletions.
13 changes: 12 additions & 1 deletion AEPServices/Mocks/MockDataQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import Foundation

public class MockDataQueue: DataQueue {

let queue = ThreadSafeArray<DataEntity>()

public init() {}
Expand All @@ -27,6 +26,18 @@ public class MockDataQueue: DataQueue {
return queue.shallowCopy.first
}

public func peek(n: Int) -> [DataEntity]? {
return Array(queue.shallowCopy[0..<n])
}

public func remove(n: Int) -> Bool {
guard let results = peek(n: n) else { return true }
for result in results {
_ = queue.filterRemove { $0.uniqueIdentifier == result.uniqueIdentifier }
}
return true
}

public func remove() -> Bool {
guard let first = peek() else { return true }
_ = queue.filterRemove { $0.uniqueIdentifier == first.uniqueIdentifier }
Expand Down
7 changes: 7 additions & 0 deletions AEPServices/Sources/dataqueue/DataQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@ import Foundation
/// Retrieves the head of this `DataQueue`, else return nil if the `DataQueue` is empty
func peek() -> DataEntity?

/// Retrieves the first `n` entries in this `DataQueue`, else return nil if the `DataQueue` is empty
func peek(n: Int) -> [DataEntity]?

/// Removes the head of this `DataQueue`
@discardableResult
func remove() -> Bool

/// Removes the first `n` entities in this `DataQueue`
@discardableResult
func remove(n: Int) -> Bool

/// Removes all stored `DataEntity` object
@discardableResult
func clear() -> Bool
Expand Down
61 changes: 38 additions & 23 deletions AEPServices/Sources/dataqueue/SQLiteDataQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import Foundation
/// - `DataEntity` objects inside this queue will be persisted in SQLite database automatically
/// - the database operations is performed in series
class SQLiteDataQueue: DataQueue {

public let databaseName: String
public let databaseFilePath: FileManager.SearchPathDirectory
public static let TABLE_NAME: String = "TB_AEP_DATA_ENTITY"
Expand Down Expand Up @@ -72,42 +71,36 @@ class SQLiteDataQueue: DataQueue {
}
}

func peek() -> DataEntity? {
if isClosed { return nil}
func peek(n: Int) -> [DataEntity]? {
guard n > 0 else { return nil }
if isClosed { return nil }
return serialQueue.sync {
let queryRowStatement = """
SELECT min(id),uniqueIdentifier,timestamp,data FROM \(SQLiteDataQueue.TABLE_NAME);
SELECT id,uniqueIdentifier,timestamp,data FROM \(SQLiteDataQueue.TABLE_NAME) ORDER BY id ASC LIMIT \(n);
"""
guard let connection = connect() else {
return nil
}
defer {
disconnect(database: connection)
}
guard let result = SQLiteWrapper.query(database: connection, sql: queryRowStatement), let firstRow = result.first else {
guard let result = SQLiteWrapper.query(database: connection, sql: queryRowStatement) else {
Log.trace(label: LOG_PREFIX, "Query returned no records: \(queryRowStatement).")
return nil
}

guard let uniqueIdentifier = firstRow[TB_KEY_UNIQUE_IDENTIFIER], let dateString = firstRow[TB_KEY_TIMESTAMP], let dataString = firstRow[TB_KEY_DATA] else {
Log.trace(label: LOG_PREFIX, "Database record did not have valid data: \(queryRowStatement).")
return nil
}
guard let dateInt64 = Int64(dateString) else {
Log.trace(label: LOG_PREFIX, "Database record had an invalid dateString: \(dateString).")
return nil
}
let date = Date(milliseconds: dateInt64)
guard !dataString.isEmpty else {
return DataEntity(uniqueIdentifier: uniqueIdentifier, timestamp: date, data: nil)
}
let data = dataString.data(using: .utf8)
return DataEntity(uniqueIdentifier: uniqueIdentifier, timestamp: date, data: data)
let entities = result.map({entityFromSQLRow(row: $0)}).compactMap {$0}
return entities
}
}

func remove() -> Bool {
if isClosed { return false}
func peek() -> DataEntity? {
return peek(n: 1)?.first
}

func remove(n: Int) -> Bool {
guard n > 0 else { return false }
if isClosed { return false }
return serialQueue.sync {
guard let connection = connect() else {
return false
Expand All @@ -116,7 +109,7 @@ class SQLiteDataQueue: DataQueue {
disconnect(database: connection)
}
let deleteRowStatement = """
DELETE FROM \(SQLiteDataQueue.TABLE_NAME) order by id limit 1;
DELETE FROM \(SQLiteDataQueue.TABLE_NAME) ORDER BY id ASC LIMIT \(n);
"""
guard SQLiteWrapper.execute(database: connection, sql: deleteRowStatement) else {
Log.warning(label: LOG_PREFIX, "Failed to delete oldest record from database: \(self.databaseName).")
Expand All @@ -126,6 +119,10 @@ class SQLiteDataQueue: DataQueue {
}
}

func remove() -> Bool {
return remove(n: 1)
}

func clear() -> Bool {
if isClosed { return false}
return serialQueue.sync {
Expand All @@ -148,7 +145,7 @@ class SQLiteDataQueue: DataQueue {
}

func count() -> Int {
if isClosed { return 0}
if isClosed { return 0 }
return serialQueue.sync {
let queryRowStatement = """
SELECT count(id) FROM \(SQLiteDataQueue.TABLE_NAME);
Expand Down Expand Up @@ -217,6 +214,24 @@ class SQLiteDataQueue: DataQueue {
return result
}
}

private func entityFromSQLRow(row: [String: String]) -> DataEntity? {
guard let uniqueIdentifier = row[TB_KEY_UNIQUE_IDENTIFIER], let dateString = row[TB_KEY_TIMESTAMP], let dataString = row[TB_KEY_DATA] else {
Log.trace(label: LOG_PREFIX, "Database record did not have valid data.")
return nil
}
guard let dateInt64 = Int64(dateString) else {
Log.trace(label: LOG_PREFIX, "Database record had an invalid dateString: \(dateString).")
return nil
}
let date = Date(milliseconds: dateInt64)
guard !dataString.isEmpty else {
return DataEntity(uniqueIdentifier: uniqueIdentifier, timestamp: date, data: nil)
}
let data = dataString.data(using: .utf8)

return DataEntity(uniqueIdentifier: uniqueIdentifier, timestamp: date, data: data)
}
}

extension Date {
Expand Down
Loading

0 comments on commit 8ace745

Please sign in to comment.