Skip to content

Commit

Permalink
Read underlying errors recursively (#3604)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/414235014887631/1208811744276031/f
Tech Design URL:
CC:

Description:

This PR updates the pixel error handling to read underlying errors recursively.
  • Loading branch information
samsymons authored Nov 23, 2024
1 parent 6abbc11 commit 3f3e9aa
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 5 deletions.
29 changes: 24 additions & 5 deletions Core/Pixel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -302,18 +302,37 @@ private extension Pixel.Event {
}

extension Dictionary where Key == String, Value == String {

mutating func appendErrorPixelParams(error: Error) {
let nsError = error as NSError

self[PixelParameters.errorCode] = "\(nsError.code)"
self[PixelParameters.errorDomain] = nsError.domain

if let underlyingError = nsError.userInfo["NSUnderlyingError"] as? NSError {
self[PixelParameters.underlyingErrorCode] = "\(underlyingError.code)"
self[PixelParameters.underlyingErrorDomain] = underlyingError.domain
let underlyingErrorParameters = underlyingErrorParameters(for: error as NSError)
self.merge(underlyingErrorParameters) { first, _ in first }
}

private func underlyingErrorParameters(for nsError: NSError, level: Int = 0) -> [String: String] {
if let underlyingError = nsError.userInfo[NSUnderlyingErrorKey] as? NSError {
let errorCodeParameterName = PixelParameters.underlyingErrorCode + (level == 0 ? "" : String(level + 1))
let errorDomainParameterName = PixelParameters.underlyingErrorDomain + (level == 0 ? "" : String(level + 1))

let currentUnderlyingErrorParameters = [
errorCodeParameterName: "\(underlyingError.code)",
errorDomainParameterName: underlyingError.domain
]

let additionalParameters = underlyingErrorParameters(for: underlyingError, level: level + 1)
return currentUnderlyingErrorParameters.merging(additionalParameters) { first, _ in first }
} else if let sqlErrorCode = nsError.userInfo["NSSQLiteErrorDomain"] as? NSNumber {
self[PixelParameters.underlyingErrorCode] = "\(sqlErrorCode.intValue)"
self[PixelParameters.underlyingErrorDomain] = "NSSQLiteErrorDomain"
return [
PixelParameters.underlyingErrorCode: "\(sqlErrorCode.intValue)",
PixelParameters.underlyingErrorDomain: "NSSQLiteErrorDomain"
]
}

return [:]
}

}
34 changes: 34 additions & 0 deletions DuckDuckGoTests/PixelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,38 @@ class PixelTests: XCTestCase {
wait(for: [firstFireExpectation, thirdFireExpectation], timeout: Double(debounceInterval + 4))
}

func testWhenDefiningUnderlyingErrorParametersThenNestedErrorsAreIncluded() {
let underlyingError4 = NSError(domain: "underlyingError4", code: 5, userInfo: [:])
let underlyingError3 = NSError(domain: "underlyingError3", code: 4, userInfo: [NSUnderlyingErrorKey: underlyingError4])
let underlyingError2 = NSError(domain: "underlyingError2", code: 3, userInfo: [NSUnderlyingErrorKey: underlyingError3])
let underlyingError1 = NSError(domain: "underlyingError1", code: 2, userInfo: [NSUnderlyingErrorKey: underlyingError2])
let error = NSError(domain: "error", code: 1, userInfo: [NSUnderlyingErrorKey: underlyingError1])

var parameters: [String: String] = [:]
parameters.appendErrorPixelParams(error: error)

XCTAssertEqual(parameters.count, 10)
XCTAssertEqual(parameters["d"], error.domain)
XCTAssertEqual(parameters["e"], String(error.code))
XCTAssertEqual(parameters["ud"], underlyingError1.domain)
XCTAssertEqual(parameters["ue"], String(underlyingError1.code))
XCTAssertEqual(parameters["ud2"], underlyingError2.domain)
XCTAssertEqual(parameters["ue2"], String(underlyingError2.code))
XCTAssertEqual(parameters["ud3"], underlyingError3.domain)
XCTAssertEqual(parameters["ue3"], String(underlyingError3.code))
XCTAssertEqual(parameters["ud4"], underlyingError4.domain)
XCTAssertEqual(parameters["ue4"], String(underlyingError4.code))
}

func testWhenDefiningUnderlyingErrorParametersAndThereIsNoUnderlyingErrorThenOnlyTopLevelParametersAreIncluded() {
let error = NSError(domain: "error", code: 1, userInfo: [:])

var parameters: [String: String] = [:]
parameters.appendErrorPixelParams(error: error)

XCTAssertEqual(parameters.count, 2)
XCTAssertEqual(parameters["d"], error.domain)
XCTAssertEqual(parameters["e"], String(error.code))
}

}

0 comments on commit 3f3e9aa

Please sign in to comment.