diff --git a/DuckDuckGo/Feedback/VPNMetadataCollector.swift b/DuckDuckGo/Feedback/VPNMetadataCollector.swift index 9d04ef794f..72ebefa9aa 100644 --- a/DuckDuckGo/Feedback/VPNMetadataCollector.swift +++ b/DuckDuckGo/Feedback/VPNMetadataCollector.swift @@ -69,7 +69,7 @@ struct VPNMetadata: Encodable { func toPrettyPrintedJSON() -> String? { let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] guard let encodedMetadata = try? encoder.encode(self) else { assertionFailure("Failed to encode metadata") @@ -81,6 +81,7 @@ struct VPNMetadata: Encodable { func toBase64() -> String { let encoder = JSONEncoder() + encoder.outputFormatting = [.sortedKeys] do { let encodedMetadata = try encoder.encode(self) diff --git a/DuckDuckGo/NetworkProtectionDebugViewController.swift b/DuckDuckGo/NetworkProtectionDebugViewController.swift index f3f98803ec..55e43909ed 100644 --- a/DuckDuckGo/NetworkProtectionDebugViewController.swift +++ b/DuckDuckGo/NetworkProtectionDebugViewController.swift @@ -45,8 +45,8 @@ final class NetworkProtectionDebugViewController: UITableViewController { Sections.networkPath: "Network Path", Sections.lastDisconnectError: "Last Disconnect Error", Sections.connectionTest: "Connection Test", - Sections.vpnConfiguration: "VPN Configuration" - + Sections.vpnConfiguration: "VPN Configuration", + Sections.vpnMetadata: "VPN Metadata", ] enum Sections: Int, CaseIterable { @@ -59,13 +59,12 @@ final class NetworkProtectionDebugViewController: UITableViewController { case networkPath case lastDisconnectError case vpnConfiguration + case vpnMetadata } enum ClearDataRows: Int, CaseIterable { - case clearAuthToken case clearAllVPNData - } enum DebugFeatureRows: Int, CaseIterable { @@ -108,6 +107,11 @@ final class NetworkProtectionDebugViewController: UITableViewController { case fullProtocolConfigurationData } + enum MetadataRows: Int, CaseIterable { + case refreshMetadata + case metadataContents + } + // MARK: Properties private let debugFeatures: NetworkProtectionDebugFeatures @@ -118,6 +122,7 @@ final class NetworkProtectionDebugViewController: UITableViewController { private var lastDisconnectError: String? private var baseConfigurationData: String? private var fullProtocolConfigurationData: String? + private var vpnMetadata: VPNMetadata? private struct ConnectionTestResult { let interface: String @@ -151,6 +156,10 @@ final class NetworkProtectionDebugViewController: UITableViewController { loadLastDisconnectError() loadConfigurationData() startPathMonitor() + + Task { + await self.refreshMetadata() + } } // MARK: Table View @@ -208,6 +217,9 @@ final class NetworkProtectionDebugViewController: UITableViewController { case .vpnConfiguration: configure(cell, forConfigurationRow: indexPath.row) + case .vpnMetadata: + configure(cell, forMetadataRow: indexPath.row) + case.none: break } @@ -215,6 +227,7 @@ final class NetworkProtectionDebugViewController: UITableViewController { return cell } + // swiftlint:disable:next cyclomatic_complexity override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch Sections(rawValue: section) { case .clearData: return ClearDataRows.allCases.count @@ -226,6 +239,7 @@ final class NetworkProtectionDebugViewController: UITableViewController { case .lastDisconnectError: return LastDisconnectErrorRows.allCases.count case .connectionTest: return ConnectionTestRows.allCases.count + connectionTestResults.count case .vpnConfiguration: return ConfigurationRows.allCases.count + case .vpnMetadata: return MetadataRows.allCases.count case .none: return 0 } @@ -260,6 +274,8 @@ final class NetworkProtectionDebugViewController: UITableViewController { } case .vpnConfiguration: break + case .vpnMetadata: + didSelectVPNMetadataAction(at: indexPath) case .none: break } @@ -548,7 +564,6 @@ final class NetworkProtectionDebugViewController: UITableViewController { case .none: assertionFailure("Couldn't map configuration row") } - } private func loadConfigurationData() { @@ -586,6 +601,42 @@ final class NetworkProtectionDebugViewController: UITableViewController { } } + // MARK: - VPN Metadata + + private func configure(_ cell: UITableViewCell, forMetadataRow row: Int) { + cell.textLabel?.font = .systemFont(ofSize: 17) + + switch MetadataRows(rawValue: row) { + case .refreshMetadata: + cell.textLabel?.text = "Refresh Metadata" + case .metadataContents: + cell.textLabel?.font = .monospacedSystemFont(ofSize: 13.0, weight: .regular) + cell.textLabel?.text = vpnMetadata?.toPrettyPrintedJSON() ?? "No Metadata" + case .none: + assertionFailure("Couldn't map configuration row") + } + } + + private func didSelectVPNMetadataAction(at indexPath: IndexPath) { + switch MetadataRows(rawValue: indexPath.row) { + case .refreshMetadata: + Task { + await refreshMetadata() + } + case .metadataContents: + break + case .none: + break + } + } + + @MainActor + private func refreshMetadata() async { + let collector = DefaultVPNMetadataCollector() + self.vpnMetadata = await collector.collectMetadata() + self.tableView.reloadData() + } + // MARK: Selection Actions private func clearAuthToken() {