From 345d655ba36a3376ae8ca1b16e9e8d4a743d38e2 Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Tue, 3 Dec 2024 17:10:51 +0100 Subject: [PATCH 01/11] Add conditional clicking --- .../Model/Actions/Click.swift | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift index a75fe0113f..b4df50e020 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift @@ -18,9 +18,60 @@ import Foundation +struct Condition: Codable, Sendable { + let left: String + let operation: String + let right: String +} + +struct Choice: Codable, Sendable { + let condition: Condition + let expect: String? + let elements: [PageElement] +} + struct ClickAction: Action { let id: String let actionType: ActionType let elements: [PageElement] let dataSource: DataSource? + let choices: [Choice]? + let defaultClick: Default? + + enum Default: Codable { + case null + case array([PageElement]) + + // Custom decoding to handle the different cases + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if container.decodeNil() { + self = .null + } else { + let array = try container.decode([PageElement].self) + self = .array(array) + } + } + + // Custom encoding to handle the different cases + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .null: + try container.encodeNil() + case .array(let array): + try container.encode(array) + } + } + } + + // Use CodingKeys so that we can use "default" as the JSON key name, which is invalid in Swift + private enum CodingKeys: String, CodingKey { + case id + case actionType + case elements + case dataSource + case choices + case defaultClick = "default" + } } From 8fa1af58ccb755b2d282042094ab3521a9be0a3a Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 4 Dec 2024 09:38:45 +0100 Subject: [PATCH 02/11] Remove unnecessary field and make elements optional --- .../Sources/DataBrokerProtection/Model/Actions/Click.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift index b4df50e020..f91cc3f240 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift @@ -26,14 +26,13 @@ struct Condition: Codable, Sendable { struct Choice: Codable, Sendable { let condition: Condition - let expect: String? let elements: [PageElement] } struct ClickAction: Action { let id: String let actionType: ActionType - let elements: [PageElement] + let elements: [PageElement]? let dataSource: DataSource? let choices: [Choice]? let defaultClick: Default? From 778111e194b9d50562528102a4f52cb35c2c2e51 Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 4 Dec 2024 10:15:15 +0100 Subject: [PATCH 03/11] Make default either nil or a container for the elements object --- .../Model/Actions/Click.swift | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift index f91cc3f240..85678cfda8 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift @@ -39,29 +39,34 @@ struct ClickAction: Action { enum Default: Codable { case null - case array([PageElement]) + case elements([PageElement]) // Custom decoding to handle the different cases init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if container.decodeNil() { + let container = try decoder.container(keyedBy: CodingKeys.self) + if container.allKeys.isEmpty { self = .null } else { - let array = try container.decode([PageElement].self) - self = .array(array) + let elements = try container.decode([PageElement].self, forKey: .elements) + self = .elements(elements) } } // Custom encoding to handle the different cases func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() + var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .null: - try container.encodeNil() - case .array(let array): - try container.encode(array) + // Encode an empty object to represent null + break + case .elements(let elements): + try container.encode(elements, forKey: .elements) } } + + private enum CodingKeys: String, CodingKey { + case elements + } } // Use CodingKeys so that we can use "default" as the JSON key name, which is invalid in Swift From adfa71cea4ccc99f36aaa9ef4ac6779b8f25dc55 Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 4 Dec 2024 17:58:11 +0100 Subject: [PATCH 04/11] Update tests --- .../DataBrokerOperationActionTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift index cc14f56b56..02722e5115 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift @@ -394,7 +394,7 @@ final class DataBrokerOperationActionTests: XCTestCase { func testWhenClickActionRuns_thenStageIsSetToSubmit() async { let mockStageCalculator = MockStageDurationCalculator() - let clickAction = ClickAction(id: "1", actionType: .click, elements: [PageElement](), dataSource: nil) + let clickAction = ClickAction(id: "1", actionType: .click, elements: [PageElement](), dataSource: nil, choices: nil, defaultClick: nil) let sut = OptOutJob( privacyConfig: PrivacyConfigurationManagingMock(), prefs: ContentScopeProperties.mock, From 92efedf704281dedeaab9ee72ea6dcb2b5a6fd9c Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 11 Dec 2024 13:32:39 +0100 Subject: [PATCH 05/11] Update click model --- .../Model/Actions/Click.swift | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift index 85678cfda8..4aa18f2548 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift @@ -35,47 +35,46 @@ struct ClickAction: Action { let elements: [PageElement]? let dataSource: DataSource? let choices: [Choice]? - let defaultClick: Default? + let `default`: Default? - enum Default: Codable { - case null - case elements([PageElement]) + let hasDefault: Bool - // Custom decoding to handle the different cases - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - if container.allKeys.isEmpty { - self = .null - } else { - let elements = try container.decode([PageElement].self, forKey: .elements) - self = .elements(elements) - } - } - - // Custom encoding to handle the different cases - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case .null: - // Encode an empty object to represent null - break - case .elements(let elements): - try container.encode(elements, forKey: .elements) - } - } + struct Default: Codable { + let elements: [PageElement]? + } - private enum CodingKeys: String, CodingKey { - case elements - } + init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decode(String.self, forKey: .id) + self.actionType = try container.decode(ActionType.self, forKey: .actionType) + self.elements = try container.decodeIfPresent([PageElement].self, forKey: .elements) + self.dataSource = try container.decodeIfPresent(DataSource.self, forKey: .dataSource) + self.choices = try container.decodeIfPresent([Choice].self, forKey: .choices) + self.default = try container.decodeIfPresent(Default.self, forKey: .default) + self.hasDefault = container.contains(.default) } - // Use CodingKeys so that we can use "default" as the JSON key name, which is invalid in Swift - private enum CodingKeys: String, CodingKey { + enum CodingKeys: String, CodingKey { case id case actionType case elements case dataSource case choices - case defaultClick = "default" + case `default` + } + + func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: .id) + try container.encode(self.actionType, forKey: .actionType) + try container.encodeIfPresent(self.elements, forKey: .elements) + try container.encodeIfPresent(self.dataSource, forKey: .dataSource) + try container.encodeIfPresent(self.choices, forKey: .choices) + + if self.hasDefault { + try container.encode(self.default, forKey: .default) + } else { + try container.encodeNil(forKey: .default) + } } } From 59b6a68244fef7f822011cbb8a0e2a6dc61c4c1c Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 11 Dec 2024 13:40:25 +0100 Subject: [PATCH 06/11] Change defaultClick -> default --- .../DataBrokerOperationActionTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift index 02722e5115..4f550d4a62 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift @@ -394,7 +394,7 @@ final class DataBrokerOperationActionTests: XCTestCase { func testWhenClickActionRuns_thenStageIsSetToSubmit() async { let mockStageCalculator = MockStageDurationCalculator() - let clickAction = ClickAction(id: "1", actionType: .click, elements: [PageElement](), dataSource: nil, choices: nil, defaultClick: nil) + let clickAction = ClickAction(id: "1", actionType: .click, elements: [PageElement](), dataSource: nil, choices: nil, default: nil) let sut = OptOutJob( privacyConfig: PrivacyConfigurationManagingMock(), prefs: ContentScopeProperties.mock, From f18732b65277af61498a0cec99ca34a81fb547e3 Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 11 Dec 2024 14:01:52 +0100 Subject: [PATCH 07/11] Add hasDefault --- .../DataBrokerOperationActionTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift index 4f550d4a62..e5c816432c 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerOperationActionTests.swift @@ -394,7 +394,7 @@ final class DataBrokerOperationActionTests: XCTestCase { func testWhenClickActionRuns_thenStageIsSetToSubmit() async { let mockStageCalculator = MockStageDurationCalculator() - let clickAction = ClickAction(id: "1", actionType: .click, elements: [PageElement](), dataSource: nil, choices: nil, default: nil) + let clickAction = ClickAction(id: "1", actionType: .click, elements: [PageElement](), dataSource: nil, choices: nil, default: nil, hasDefault: false) let sut = OptOutJob( privacyConfig: PrivacyConfigurationManagingMock(), prefs: ContentScopeProperties.mock, From 216f195fc344f8c51602774d4c4e1a74eee02aca Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 11 Dec 2024 14:18:18 +0100 Subject: [PATCH 08/11] Convert to class? --- .../Sources/DataBrokerProtection/Model/Actions/Click.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift index 4aa18f2548..0e31a9abc7 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift @@ -29,7 +29,7 @@ struct Choice: Codable, Sendable { let elements: [PageElement] } -struct ClickAction: Action { +final class ClickAction: Action { let id: String let actionType: ActionType let elements: [PageElement]? From eb6e89aff657c22af629818df5f1ff5e80f8a38f Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 11 Dec 2024 14:22:49 +0100 Subject: [PATCH 09/11] Revert "Convert to class?" This reverts commit 216f195fc344f8c51602774d4c4e1a74eee02aca. --- .../Sources/DataBrokerProtection/Model/Actions/Click.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift index 0e31a9abc7..4aa18f2548 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift @@ -29,7 +29,7 @@ struct Choice: Codable, Sendable { let elements: [PageElement] } -final class ClickAction: Action { +struct ClickAction: Action { let id: String let actionType: ActionType let elements: [PageElement]? From 476a9609bf0270862083c1d2dfc9334ab813997f Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Thu, 12 Dec 2024 10:59:05 +0100 Subject: [PATCH 10/11] Add init --- .../DataBrokerProtection/Model/Actions/Click.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift index 4aa18f2548..ce44706127 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift @@ -42,6 +42,16 @@ struct ClickAction: Action { struct Default: Codable { let elements: [PageElement]? } + + init(id: String, actionType: ActionType, elements: [PageElement]? = nil, dataSource: DataSource? = nil, choices: [Choice]? = nil, `default`: Default? = nil, hasDefault: Bool = false) { + self.id = id + self.actionType = actionType + self.elements = elements + self.dataSource = dataSource + self.choices = choices + self.default = `default` + self.hasDefault = `default` != nil + } init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) From f0d371dfec070fedc461f37d5da669f7a8b6c0c4 Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Thu, 12 Dec 2024 11:19:28 +0100 Subject: [PATCH 11/11] Remove whitespace --- .../Sources/DataBrokerProtection/Model/Actions/Click.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift index ce44706127..e237d32bfb 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift @@ -42,7 +42,7 @@ struct ClickAction: Action { struct Default: Codable { let elements: [PageElement]? } - + init(id: String, actionType: ActionType, elements: [PageElement]? = nil, dataSource: DataSource? = nil, choices: [Choice]? = nil, `default`: Default? = nil, hasDefault: Bool = false) { self.id = id self.actionType = actionType