diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerOperationsCollection.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerOperationsCollection.swift index cbe22fe8fe..2f4cb38441 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerOperationsCollection.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerOperationsCollection.swift @@ -19,13 +19,91 @@ import Foundation import Common -protocol DataBrokerOperationsCollectionErrorDelegate: AnyObject { - func dataBrokerOperationsCollection(_ dataBrokerOperationsCollection: DataBrokerOperationsCollection, - didError error: Error, - whileRunningBrokerOperationData: BrokerOperationData, - withDataBrokerName dataBrokerName: String?) - func dataBrokerOperationsCollection(_ dataBrokerOperationsCollection: DataBrokerOperationsCollection, - didErrorBeforeStartingBrokerOperations error: Error) +protocol DataBrokerOperationsBuilder { + func createDataBrokerOperationCollections(from brokerProfileQueriesData: [BrokerProfileQueryData], + operationType: DataBrokerOperationsCollection.OperationType, + priorityDate: Date?, + showWebView: Bool, + database: DataBrokerProtectionRepository, + intervalBetweenOperations: TimeInterval?, + notificationCenter: NotificationCenter, + runner: WebOperationRunner, + pixelHandler: EventMapping, + userNotificationService: DataBrokerProtectionUserNotificationService) -> [DataBrokerOperationsCollection] + +} + +final class DefaultDataBrokerOperationsBuilder: DataBrokerOperationsBuilder { + func createDataBrokerOperationCollections(from brokerProfileQueriesData: [BrokerProfileQueryData], + operationType: DataBrokerOperationsCollection.OperationType, + priorityDate: Date?, + showWebView: Bool, + database: DataBrokerProtectionRepository, + intervalBetweenOperations: TimeInterval? = nil, + notificationCenter: NotificationCenter = NotificationCenter.default, + runner: WebOperationRunner, + pixelHandler: EventMapping, + userNotificationService: DataBrokerProtectionUserNotificationService) -> [DataBrokerOperationsCollection] { + + var collections: [DataBrokerOperationsCollection] = [] + var visitedDataBrokerIDs: Set = [] + + for queryData in brokerProfileQueriesData { + + guard let dataBrokerID = queryData.dataBroker.id else { continue } + + let groupedBrokerQueries = brokerProfileQueriesData.filter { $0.dataBroker.id == dataBrokerID } + let filteredAndSortedOperationsData = filterAndSortOperationsData(brokerProfileQueriesData: groupedBrokerQueries, + operationType: operationType, + priorityDate: priorityDate) + + if !visitedDataBrokerIDs.contains(dataBrokerID) { + let collection = DataBrokerOperationsCollection(database: database, + operationType: operationType, + intervalBetweenOperations: intervalBetweenOperations, + priorityDate: priorityDate, + notificationCenter: notificationCenter, + runner: runner, + pixelHandler: pixelHandler, + userNotificationService: userNotificationService, + operationData: filteredAndSortedOperationsData, + profileQueryData: groupedBrokerQueries, + showWebView: showWebView) + collections.append(collection) + + visitedDataBrokerIDs.insert(dataBrokerID) + } + } + + return collections + } + + private func filterAndSortOperationsData(brokerProfileQueriesData: [BrokerProfileQueryData], + operationType: DataBrokerOperationsCollection.OperationType, + priorityDate: Date?) -> [BrokerOperationData] { + let operationsData: [BrokerOperationData] + + switch operationType { + case .optOut: + operationsData = brokerProfileQueriesData.flatMap { $0.optOutOperationsData } + case .scan: + operationsData = brokerProfileQueriesData.compactMap { $0.scanOperationData } + case .all: + operationsData = brokerProfileQueriesData.flatMap { $0.operationsData } + } + + let filteredAndSortedOperationsData: [BrokerOperationData] + + if let priorityDate = priorityDate { + filteredAndSortedOperationsData = operationsData + .filter { $0.preferredRunDate != nil && $0.preferredRunDate! <= priorityDate } + .sorted { $0.preferredRunDate! < $1.preferredRunDate! } + } else { + filteredAndSortedOperationsData = operationsData + } + + return filteredAndSortedOperationsData + } } final class DataBrokerOperationsCollection: Operation { @@ -37,13 +115,12 @@ final class DataBrokerOperationsCollection: Operation { } public var error: Error? - public weak var errorDelegate: DataBrokerOperationsCollectionErrorDelegate? - private let dataBrokerID: Int64 - private let database: DataBrokerProtectionRepository private let id = UUID() private var _isExecuting = false private var _isFinished = false + + private let database: DataBrokerProtectionRepository private let intervalBetweenOperations: TimeInterval? // The time in seconds to wait in-between operations private let priorityDate: Date? // The date to filter and sort operations priorities private let operationType: OperationType @@ -52,23 +129,25 @@ final class DataBrokerOperationsCollection: Operation { private let pixelHandler: EventMapping private let showWebView: Bool private let userNotificationService: DataBrokerProtectionUserNotificationService + private let operationData: [BrokerOperationData] + private let profileQueryData: [BrokerProfileQueryData] deinit { os_log("Deinit operation: %{public}@", log: .dataBrokerProtection, String(describing: id.uuidString)) } - init(dataBrokerID: Int64, - database: DataBrokerProtectionRepository, + init(database: DataBrokerProtectionRepository, operationType: OperationType, - intervalBetweenOperations: TimeInterval? = nil, + intervalBetweenOperations: TimeInterval?, priorityDate: Date? = nil, notificationCenter: NotificationCenter = NotificationCenter.default, runner: WebOperationRunner, pixelHandler: EventMapping, userNotificationService: DataBrokerProtectionUserNotificationService, + operationData: [BrokerOperationData], + profileQueryData: [BrokerProfileQueryData], showWebView: Bool) { - self.dataBrokerID = dataBrokerID self.database = database self.intervalBetweenOperations = intervalBetweenOperations self.priorityDate = priorityDate @@ -78,6 +157,8 @@ final class DataBrokerOperationsCollection: Operation { self.pixelHandler = pixelHandler self.showWebView = showWebView self.userNotificationService = userNotificationService + self.operationData = operationData + self.profileQueryData = profileQueryData super.init() } @@ -113,58 +194,15 @@ final class DataBrokerOperationsCollection: Operation { } } - private func filterAndSortOperationsData(brokerProfileQueriesData: [BrokerProfileQueryData], operationType: OperationType, priorityDate: Date?) -> [BrokerOperationData] { - let operationsData: [BrokerOperationData] - - switch operationType { - case .optOut: - operationsData = brokerProfileQueriesData.flatMap { $0.optOutOperationsData } - case .scan: - operationsData = brokerProfileQueriesData.compactMap { $0.scanOperationData } - case .all: - operationsData = brokerProfileQueriesData.flatMap { $0.operationsData } - } - - let filteredAndSortedOperationsData: [BrokerOperationData] - - if let priorityDate = priorityDate { - filteredAndSortedOperationsData = operationsData - .filter { $0.preferredRunDate != nil && $0.preferredRunDate! <= priorityDate } - .sorted { $0.preferredRunDate! < $1.preferredRunDate! } - } else { - filteredAndSortedOperationsData = operationsData - } - - return filteredAndSortedOperationsData - } - - // swiftlint:disable:next function_body_length private func runOperation() async { - let allBrokerProfileQueryData: [BrokerProfileQueryData] - - do { - allBrokerProfileQueryData = try database.fetchAllBrokerProfileQueryData() - } catch { - os_log("DataBrokerOperationsCollection error: runOperation, error: %{public}@", log: .error, error.localizedDescription) - errorDelegate?.dataBrokerOperationsCollection(self, didErrorBeforeStartingBrokerOperations: error) - return - } - let brokerProfileQueriesData = allBrokerProfileQueryData.filter { $0.dataBroker.id == dataBrokerID } - - let filteredAndSortedOperationsData = filterAndSortOperationsData(brokerProfileQueriesData: brokerProfileQueriesData, - operationType: operationType, - priorityDate: priorityDate) - - os_log("filteredAndSortedOperationsData count: %{public}d for brokerID %{public}d", log: .dataBrokerProtection, filteredAndSortedOperationsData.count, dataBrokerID) - - for operationData in filteredAndSortedOperationsData { + for operationData in operationData { if isCancelled { os_log("Cancelled operation, returning...", log: .dataBrokerProtection) return } - let brokerProfileData = brokerProfileQueriesData.filter { + let brokerProfileData = profileQueryData.filter { $0.dataBroker.id == operationData.brokerId && $0.profileQuery.id == operationData.profileQueryId }.first @@ -196,10 +234,10 @@ final class DataBrokerOperationsCollection: Operation { } catch { os_log("Error: %{public}@", log: .dataBrokerProtection, error.localizedDescription) self.error = error - errorDelegate?.dataBrokerOperationsCollection(self, - didError: error, - whileRunningBrokerOperationData: operationData, - withDataBrokerName: brokerProfileQueriesData.first?.dataBroker.name) + if let error = error as? DataBrokerProtectionError, + let dataBrokerName = profileQueryData.first?.dataBroker.name { + pixelHandler.fire(.error(error: error, dataBroker: dataBrokerName)) + } } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionProcessor.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionProcessor.swift index f02d472666..f2ca4b9bc8 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionProcessor.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionProcessor.swift @@ -34,13 +34,15 @@ final class DataBrokerProtectionProcessor { private let userNotificationService: DataBrokerProtectionUserNotificationService private let engagementPixels: DataBrokerProtectionEngagementPixels private let eventPixels: DataBrokerProtectionEventPixels + private let operationsBuilder: DataBrokerOperationsBuilder init(database: DataBrokerProtectionRepository, config: SchedulerConfig, operationRunnerProvider: OperationRunnerProvider, notificationCenter: NotificationCenter = NotificationCenter.default, pixelHandler: EventMapping, - userNotificationService: DataBrokerProtectionUserNotificationService) { + userNotificationService: DataBrokerProtectionUserNotificationService, + operationsBuilder: DataBrokerOperationsBuilder = DefaultDataBrokerOperationsBuilder()) { self.database = database self.config = config @@ -52,6 +54,7 @@ final class DataBrokerProtectionProcessor { self.userNotificationService = userNotificationService self.engagementPixels = DataBrokerProtectionEngagementPixels(database: database, handler: pixelHandler) self.eventPixels = DataBrokerProtectionEventPixels(database: database, handler: pixelHandler) + self.operationsBuilder = operationsBuilder } // MARK: - Public functions @@ -129,10 +132,16 @@ final class DataBrokerProtectionProcessor { do { let brokersProfileData = try database.fetchAllBrokerProfileQueryData() - dataBrokerOperationCollections = createDataBrokerOperationCollections(from: brokersProfileData, - operationType: operationType, - priorityDate: priorityDate, - showWebView: showWebView) + dataBrokerOperationCollections = self.operationsBuilder.createDataBrokerOperationCollections(from: brokersProfileData, + operationType: operationType, + priorityDate: priorityDate, + showWebView: showWebView, + database: database, + intervalBetweenOperations: 2, + notificationCenter: .default, + runner: operationRunnerProvider.getOperationRunner(), + pixelHandler: pixelHandler, + userNotificationService: userNotificationService) for collection in dataBrokerOperationCollections { operationQueue.addOperation(collection) @@ -152,58 +161,7 @@ final class DataBrokerProtectionProcessor { } } - private func createDataBrokerOperationCollections(from brokerProfileQueriesData: [BrokerProfileQueryData], - operationType: DataBrokerOperationsCollection.OperationType, - priorityDate: Date?, - showWebView: Bool) -> [DataBrokerOperationsCollection] { - - var collections: [DataBrokerOperationsCollection] = [] - var visitedDataBrokerIDs: Set = [] - - for queryData in brokerProfileQueriesData { - guard let dataBrokerID = queryData.dataBroker.id else { continue } - - if !visitedDataBrokerIDs.contains(dataBrokerID) { - let collection = DataBrokerOperationsCollection(dataBrokerID: dataBrokerID, - database: database, - operationType: operationType, - intervalBetweenOperations: config.intervalBetweenSameBrokerOperations, - priorityDate: priorityDate, - notificationCenter: notificationCenter, - runner: operationRunnerProvider.getOperationRunner(), - pixelHandler: pixelHandler, - userNotificationService: userNotificationService, - showWebView: showWebView) - collection.errorDelegate = self - collections.append(collection) - - visitedDataBrokerIDs.insert(dataBrokerID) - } - } - - return collections - } - deinit { os_log("Deinit DataBrokerProtectionProcessor", log: .dataBrokerProtection) } } - -extension DataBrokerProtectionProcessor: DataBrokerOperationsCollectionErrorDelegate { - - func dataBrokerOperationsCollection(_ dataBrokerOperationsCollection: DataBrokerOperationsCollection, didErrorBeforeStartingBrokerOperations error: Error) { - - } - - func dataBrokerOperationsCollection(_ dataBrokerOperationsCollection: DataBrokerOperationsCollection, - didError error: Error, - whileRunningBrokerOperationData: BrokerOperationData, - withDataBrokerName dataBrokerName: String?) { - if let error = error as? DataBrokerProtectionError, - let dataBrokerName = dataBrokerName { - pixelHandler.fire(.error(error: error, dataBroker: dataBrokerName)) - } else { - os_log("Cant handle error", log: .dataBrokerProtection) - } - } -}