Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DBP test case #3554

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public protocol DataBrokerProtectionRepository {
func updatePreferredRunDate(_ date: Date?, brokerId: Int64, profileQueryId: Int64, extractedProfileId: Int64) throws
func updateLastRunDate(_ date: Date?, brokerId: Int64, profileQueryId: Int64) throws
func updateLastRunDate(_ date: Date?, brokerId: Int64, profileQueryId: Int64, extractedProfileId: Int64) throws
func updateAttemptCount(_ count: Int64, brokerId: Int64, profileQueryId: Int64, extractedProfileId: Int64) throws
func incrementAttemptCount(brokerId: Int64, profileQueryId: Int64, extractedProfileId: Int64) throws
func updateSubmittedSuccessfullyDate(_ date: Date?,
forBrokerId brokerId: Int64,
profileQueryId: Int64,
Expand All @@ -62,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
}
Expand Down Expand Up @@ -238,6 +241,37 @@ final class DataBrokerProtectionDatabase: DataBrokerProtectionRepository {
}
}

func updateAttemptCount(_ count: Int64, brokerId: Int64, profileQueryId: Int64, extractedProfileId: Int64) throws {
do {
let vault = try self.vault ?? DataBrokerProtectionSecureVaultFactory.makeVault(reporter: secureVaultErrorReporter)
try vault.updateAttemptCount(
count,
brokerId: brokerId,
profileQueryId: profileQueryId,
extractedProfileId: extractedProfileId
)
} catch {
Logger.dataBrokerProtection.error("Database error: updateAttemptCount, error: \(error.localizedDescription, privacy: .public)")
pixelHandler.fire(.generalError(error: error, functionOccurredIn: "DataBrokerProtectionDatabase.updateAttemptCount"))
throw error
}
}

func incrementAttemptCount(brokerId: Int64, profileQueryId: Int64, extractedProfileId: Int64) throws {
do {
let vault = try self.vault ?? DataBrokerProtectionSecureVaultFactory.makeVault(reporter: secureVaultErrorReporter)
try vault.incrementAttemptCount(
brokerId: brokerId,
profileQueryId: profileQueryId,
extractedProfileId: extractedProfileId
)
} catch {
Logger.dataBrokerProtection.error("Database error: incrementAttemptCount, error: \(error.localizedDescription, privacy: .public)")
pixelHandler.fire(.generalError(error: error, functionOccurredIn: "DataBrokerProtectionDatabase.incrementAttemptCount"))
throw error
}
}

func updateSubmittedSuccessfullyDate(_ date: Date?,
forBrokerId brokerId: Int64,
profileQueryId: Int64,
Expand Down Expand Up @@ -388,6 +422,7 @@ final class DataBrokerProtectionDatabase: DataBrokerProtectionRepository {
createdDate: optOut.createdDate,
lastRunDate: optOut.lastRunDate,
preferredRunDate: optOut.preferredRunDate,
attemptCount: optOut.attemptCount,
submittedSuccessfullyDate: optOut.submittedSuccessfullyDate,
sevenDaysConfirmationPixelFired: optOut.sevenDaysConfirmationPixelFired,
fourteenDaysConfirmationPixelFired: optOut.fourteenDaysConfirmationPixelFired,
Expand Down Expand Up @@ -453,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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]
}
}
}
Expand Down Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public struct OptOutJobData: BrokerJobData, Sendable {
let preferredRunDate: Date?
let historyEvents: [HistoryEvent]
let lastRunDate: Date?
let attemptCount: Int64

// This was added in a later DB migration (V4), so will be nil for older entries submitted before the migration
let submittedSuccessfullyDate: Date?
Expand All @@ -89,6 +90,7 @@ public struct OptOutJobData: BrokerJobData, Sendable {
preferredRunDate: Date? = nil,
historyEvents: [HistoryEvent],
lastRunDate: Date? = nil,
attemptCount: Int64,
submittedSuccessfullyDate: Date? = nil,
extractedProfile: ExtractedProfile,
sevenDaysConfirmationPixelFired: Bool = false,
Expand All @@ -100,6 +102,7 @@ public struct OptOutJobData: BrokerJobData, Sendable {
self.preferredRunDate = preferredRunDate
self.historyEvents = historyEvents
self.lastRunDate = lastRunDate
self.attemptCount = attemptCount
self.submittedSuccessfullyDate = submittedSuccessfullyDate
self.extractedProfile = extractedProfile
self.sevenDaysConfirmationPixelFired = sevenDaysConfirmationPixelFired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct DataBrokerScheduleConfig: Codable {
let retryError: Int
let confirmOptOutScan: Int
let maintenanceScan: Int
let maxAttempts: Int
}

extension Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ struct DataBrokerProfileQueryOperationManager: OperationsManager {
createdDate: Date(),
preferredRunDate: preferredRunOperation,
historyEvents: [HistoryEvent](),
attemptCount: 0,
submittedSuccessfullyDate: nil,
extractedProfile: extractedProfile,
sevenDaysConfirmationPixelFired: false,
Expand Down Expand Up @@ -357,6 +358,12 @@ struct DataBrokerProfileQueryOperationManager: OperationsManager {
lastStageDate: stageDurationCalculator.lastStateTime,
startTime: stageDurationCalculator.startTime)
try database.add(.init(extractedProfileId: extractedProfileId, brokerId: brokerId, profileQueryId: profileQueryId, type: .optOutRequested))
try incrementAttemptCountIfNeeded(
database: database,
brokerId: brokerId,
profileQueryId: profileQueryId,
extractedProfileId: extractedProfileId
)
} catch {
let tries = try? retriesCalculatorUseCase.calculateForOptOut(database: database, brokerId: brokerId, profileQueryId: profileQueryId, extractedProfileId: extractedProfileId)
stageDurationCalculator.fireOptOutFailure(tries: tries ?? -1)
Expand Down Expand Up @@ -389,6 +396,18 @@ struct DataBrokerProfileQueryOperationManager: OperationsManager {
schedulingConfig: schedulingConfig)
}

private func incrementAttemptCountIfNeeded(database: DataBrokerProtectionRepository,
brokerId: Int64,
profileQueryId: Int64,
extractedProfileId: Int64) throws {
guard let events = try? database.fetchOptOutHistoryEvents(brokerId: brokerId, profileQueryId: profileQueryId, extractedProfileId: extractedProfileId),
events.max(by: { $0.date < $1.date })?.type == .optOutRequested else {
return
}

try database.incrementAttemptCount(brokerId: brokerId, profileQueryId: profileQueryId, extractedProfileId: extractedProfileId)
}

private func handleOperationError(origin: OperationPreferredDateUpdaterOrigin,
brokerId: Int64,
profileQueryId: Int64,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,18 @@ public struct DefaultDataBrokerProtectionBrokerUpdater: DataBrokerProtectionBrok
guard let savedBrokerId = savedBroker.id else { return }

try vault.update(broker, with: savedBrokerId)
try updateAttemptCount(broker)
}
}

private func updateAttemptCount(_ broker: DataBroker) throws {
guard broker.type == .parent, let brokerId = broker.id else { return }

let optOutJobs = try vault.fetchOptOuts(brokerId: brokerId)
for optOutJob in optOutJobs {
if let extractedProfileId = optOutJob.extractedProfile.id {
try vault.updateAttemptCount(0, brokerId: brokerId, profileQueryId: optOutJob.profileQueryId, extractedProfileId: extractedProfileId)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct OperationPreferredDateCalculator {
historyEvents: [HistoryEvent],
extractedProfileID: Int64?,
schedulingConfig: DataBrokerScheduleConfig,
attemptCount: Int64?,
date: DateProtocol = SystemDate()) throws -> Date? {
guard let lastEvent = historyEvents.last else {
throw DataBrokerProtectionError.cantCalculatePreferredRunDate
Expand All @@ -70,7 +71,8 @@ struct OperationPreferredDateCalculator {
case .matchesFound, .reAppearence:
if let extractedProfileID = extractedProfileID, shouldScheduleNewOptOut(events: historyEvents,
extractedProfileId: extractedProfileID,
schedulingConfig: schedulingConfig) {
schedulingConfig: schedulingConfig,
attemptCount: attemptCount) {
return date.now
} else {
return currentPreferredRunDate
Expand All @@ -84,10 +86,18 @@ struct OperationPreferredDateCalculator {
}
}

// If the time elapsed since the last profile removal exceeds the current date plus maintenance period (expired), we should proceed with scheduling a new opt-out request as the broker has failed to honor the previous one.
// If the time elapsed since the last profile removal exceeds the current date plus maintenance period (expired),
// and the number of attempts is still fewer than the configurable limit,
// we should proceed with scheduling a new opt-out request as the broker has failed to honor the previous one.
private func shouldScheduleNewOptOut(events: [HistoryEvent],
extractedProfileId: Int64,
schedulingConfig: DataBrokerScheduleConfig) -> Bool {
schedulingConfig: DataBrokerScheduleConfig,
attemptCount: Int64?) -> Bool {
let currentAttempt = attemptCount ?? 0
if schedulingConfig.maxAttempts != -1, currentAttempt >= schedulingConfig.maxAttempts {
return false
}

guard let lastRemovalEvent = events.last(where: { $0.type == .optOutRequested && $0.extractedProfileId == extractedProfileId }) else {
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ struct OperationPreferredDateUpdaterUseCase: OperationPreferredDateUpdater {
var newOptOutPreferredDate = try calculator.dateForOptOutOperation(currentPreferredRunDate: currentOptOutPreferredRunDate,
historyEvents: brokerProfileQuery.events,
extractedProfileID: extractedProfileId,
schedulingConfig: schedulingConfig)
schedulingConfig: schedulingConfig,
attemptCount: optOutJob?.attemptCount)

if let newDate = newOptOutPreferredDate, origin == .scan {
newOptOutPreferredDate = returnMostRecentDate(currentOptOutPreferredRunDate, newDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 0,
"maintenanceScan": 240
"maintenanceScan": 120
},
"addedDatetime": 1725632531153,
"version": "0.0.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"schedulingConfig": {
"retryError": 48,
"confirmOptOutScan": 72,
"maintenanceScan": 240
"maintenanceScan": 120,
"maxAttempts": -1
}
}
Loading
Loading