diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionDatabase.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionDatabase.swift index cf46f74afc..3588edbfe8 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionDatabase.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionDatabase.swift @@ -64,6 +64,7 @@ public protocol DataBrokerProtectionRepository { func fetchOptOutHistoryEvents(brokerId: Int64, profileQueryId: Int64, extractedProfileId: Int64) throws -> [HistoryEvent] func hasMatches() throws -> Bool + func fetchAllAttempts() throws -> [AttemptInformation] func fetchAttemptInformation(for extractedProfileId: Int64) throws -> AttemptInformation? func addAttempt(extractedProfileId: Int64, attemptUUID: UUID, dataBroker: String, lastStageDate: Date, startTime: Date) throws } @@ -487,6 +488,17 @@ final class DataBrokerProtectionDatabase: DataBrokerProtectionRepository { } } + func fetchAllAttempts() throws -> [AttemptInformation] { + do { + let vault = try self.vault ?? DataBrokerProtectionSecureVaultFactory.makeVault(reporter: secureVaultErrorReporter) + return try vault.fetchAllAttempts() + } catch { + Logger.dataBrokerProtection.error("Database error: fetchAllAttempts, error: \(error.localizedDescription, privacy: .public)") + pixelHandler.fire(.generalError(error: error, functionOccurredIn: "DataBrokerProtectionDatabase.fetchAllAttempts")) + throw error + } + } + func fetchAttemptInformation(for extractedProfileId: Int64) throws -> AttemptInformation? { do { let vault = try self.vault ?? DataBrokerProtectionSecureVaultFactory.makeVault(reporter: secureVaultErrorReporter) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserViewModel.swift index 36eaf1b8ac..fe002d8d12 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserViewModel.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserViewModel.swift @@ -48,7 +48,8 @@ final class DataBrokerDatabaseBrowserViewModel: ObservableObject { guard let dataManager = self.dataManager else { return } Task { - guard let data = try? dataManager.fetchBrokerProfileQueryData(ignoresCache: true) else { + guard let data = try? dataManager.fetchBrokerProfileQueryData(ignoresCache: true), + let attempts = try? dataManager.fetchAllOptOutAttempts() else { assertionFailure("DataManager error during DataBrokerDatavaseBrowserViewModel.updateTables") return } @@ -68,9 +69,10 @@ final class DataBrokerDatabaseBrowserViewModel: ObservableObject { let optOutsTable = createTable(using: optOutJobs, tableName: "OptOutOperation") let extractedProfilesTable = createTable(using: extractedProfiles, tableName: "ExtractedProfile") let eventsTable = createTable(using: events.sorted(by: { $0.date < $1.date }), tableName: "Events") + let attemptsTable = createTable(using: attempts.sorted(by: <), tableName: "OptOutAttempts") DispatchQueue.main.async { - self.tables = [brokersTable, profileQueriesTable, scansTable, optOutsTable, extractedProfilesTable, eventsTable] + self.tables = [brokersTable, profileQueriesTable, scansTable, optOutsTable, extractedProfilesTable, eventsTable, attemptsTable] } } } @@ -136,3 +138,25 @@ struct DataBrokerDatabaseBrowserData { } } + +extension DataBrokerProtectionDataManager { + func fetchAllOptOutAttempts() throws -> [AttemptInformation] { + try database.fetchAllAttempts() + } +} + +extension AttemptInformation: Comparable { + public static func < (lhs: AttemptInformation, rhs: AttemptInformation) -> Bool { + if lhs.extractedProfileId != rhs.extractedProfileId { + return lhs.extractedProfileId < rhs.extractedProfileId + } else if lhs.dataBroker != rhs.dataBroker { + return lhs.dataBroker < rhs.dataBroker + } else { + return lhs.startDate < rhs.startDate + } + } + + public static func == (lhs: AttemptInformation, rhs: AttemptInformation) -> Bool { + lhs.attemptId == rhs.attemptId + } +} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionDatabaseProvider.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionDatabaseProvider.swift index 15f9e7a858..089034df70 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionDatabaseProvider.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionDatabaseProvider.swift @@ -100,6 +100,7 @@ protocol DataBrokerProtectionDatabaseProvider: SecureStorageDatabaseProvider { func hasMatches() throws -> Bool + func fetchAllAttempts() throws -> [OptOutAttemptDB] func fetchAttemptInformation(for extractedProfileId: Int64) throws -> OptOutAttemptDB? func save(_ optOutAttemptDB: OptOutAttemptDB) throws } @@ -619,6 +620,12 @@ final class DefaultDataBrokerProtectionDatabaseProvider: GRDBSecureStorageDataba } } + func fetchAllAttempts() throws -> [OptOutAttemptDB] { + try db.read { db in + return try OptOutAttemptDB.fetchAll(db) + } + } + func fetchAttemptInformation(for extractedProfileId: Int64) throws -> OptOutAttemptDB? { try db.read { db in return try OptOutAttemptDB.fetchOne(db, key: extractedProfileId) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionSecureVault.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionSecureVault.swift index 4fd78c5c32..3cf0b4d9bb 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionSecureVault.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionSecureVault.swift @@ -107,6 +107,7 @@ protocol DataBrokerProtectionSecureVault: SecureVault { func hasMatches() throws -> Bool + func fetchAllAttempts() throws -> [AttemptInformation] func fetchAttemptInformation(for extractedProfileId: Int64) throws -> AttemptInformation? func save(extractedProfileId: Int64, attemptUUID: UUID, dataBroker: String, lastStageDate: Date, startTime: Date) throws } @@ -445,6 +446,11 @@ final class DefaultDataBrokerProtectionSecureVault [AttemptInformation] { + let mapper = MapperToModel(mechanism: l2Decrypt(data:)) + return try self.providers.database.fetchAllAttempts().map(mapper.mapToModel(_:)) + } + func fetchAttemptInformation(for extractedProfileId: Int64) throws -> AttemptInformation? { let mapper = MapperToModel(mechanism: l2Decrypt(data:)) if let attemptDB = try self.providers.database.fetchAttemptInformation(for: extractedProfileId) { diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift index 827f21fd18..ecb5816f94 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift @@ -769,6 +769,10 @@ final class DataBrokerProtectionSecureVaultMock: DataBrokerProtectionSecureVault return 1 } + func fetchAllAttempts() throws -> [AttemptInformation] { + [] + } + func fetchAttemptInformation(for extractedProfileId: Int64) throws -> AttemptInformation? { return nil } @@ -990,6 +994,10 @@ final class MockDatabase: DataBrokerProtectionRepository { return extractedProfilesFromBroker } + func fetchAllAttempts() throws -> [AttemptInformation] { + [attemptInformation].compactMap { $0 } + } + func fetchAttemptInformation(for extractedProfileId: Int64) -> AttemptInformation? { return attemptInformation }