Skip to content

Commit 8710c7a

Browse files
authored
[Functions] Include endpoint and region details in error messages (#14487)
1 parent 0ea9a19 commit 8710c7a

File tree

3 files changed

+41
-14
lines changed

3 files changed

+41
-14
lines changed

FirebaseFunctions/Sources/Functions.swift

+19-9
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,9 @@ enum FunctionsConstants {
401401

402402
do {
403403
let rawData = try await fetcher.beginFetch()
404-
return try callableResult(fromResponseData: rawData)
404+
return try callableResult(fromResponseData: rawData, endpointURL: url)
405405
} catch {
406-
throw processedError(fromResponseError: error)
406+
throw processedError(fromResponseError: error, endpointURL: url)
407407
}
408408
}
409409

@@ -454,10 +454,10 @@ enum FunctionsConstants {
454454
fetcher.beginFetch { [self] data, error in
455455
let result: Result<HTTPSCallableResult, any Error>
456456
if let error {
457-
result = .failure(processedError(fromResponseError: error))
457+
result = .failure(processedError(fromResponseError: error, endpointURL: url))
458458
} else if let data {
459459
do {
460-
result = try .success(callableResult(fromResponseData: data))
460+
result = try .success(callableResult(fromResponseData: data, endpointURL: url))
461461
} catch {
462462
result = .failure(error)
463463
}
@@ -523,11 +523,14 @@ enum FunctionsConstants {
523523
return fetcher
524524
}
525525

526-
private func processedError(fromResponseError error: any Error) -> any Error {
526+
private func processedError(fromResponseError error: any Error,
527+
endpointURL url: URL) -> any Error {
527528
let error = error as NSError
528529
let localError: (any Error)? = if error.domain == kGTMSessionFetcherStatusDomain {
529530
FunctionsError(
530531
httpStatusCode: error.code,
532+
region: region,
533+
url: url,
531534
body: error.userInfo["data"] as? Data,
532535
serializer: serializer
533536
)
@@ -538,16 +541,23 @@ enum FunctionsConstants {
538541
return localError ?? error
539542
}
540543

541-
private func callableResult(fromResponseData data: Data) throws -> HTTPSCallableResult {
542-
let processedData = try processedData(fromResponseData: data)
544+
private func callableResult(fromResponseData data: Data,
545+
endpointURL url: URL) throws -> HTTPSCallableResult {
546+
let processedData = try processedData(fromResponseData: data, endpointURL: url)
543547
let json = try responseDataJSON(from: processedData)
544548
let payload = try serializer.decode(json)
545549
return HTTPSCallableResult(data: payload)
546550
}
547551

548-
private func processedData(fromResponseData data: Data) throws -> Data {
552+
private func processedData(fromResponseData data: Data, endpointURL url: URL) throws -> Data {
549553
// `data` might specify a custom error. If so, throw the error.
550-
if let bodyError = FunctionsError(httpStatusCode: 200, body: data, serializer: serializer) {
554+
if let bodyError = FunctionsError(
555+
httpStatusCode: 200,
556+
region: region,
557+
url: url,
558+
body: data,
559+
serializer: serializer
560+
) {
551561
throw bodyError
552562
}
553563

FirebaseFunctions/Sources/FunctionsError.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ struct FunctionsError: CustomNSError {
180180
/// }
181181
/// ```
182182
/// - serializer: The `FunctionsSerializer` used to decode `details` in the error body.
183-
init?(httpStatusCode: Int, body: Data?, serializer: FunctionsSerializer) {
183+
init?(httpStatusCode: Int, region: String, url: URL, body: Data?,
184+
serializer: FunctionsSerializer) {
184185
// Start with reasonable defaults from the status code.
185186
var code = FunctionsErrorCode(httpStatusCode: httpStatusCode)
186187
var description = Self.errorDescription(from: code)
@@ -224,6 +225,8 @@ struct FunctionsError: CustomNSError {
224225

225226
var userInfo = [String: Any]()
226227
userInfo[NSLocalizedDescriptionKey] = description
228+
userInfo["region"] = region
229+
userInfo["url"] = url
227230
if let details {
228231
userInfo[FunctionsErrorDetailsKey] = details
229232
}

FirebaseFunctions/Tests/Unit/FunctionsErrorTests.swift

+18-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ final class FunctionsErrorTests: XCTestCase {
4545
// The error should be `nil`.
4646
let error = FunctionsError(
4747
httpStatusCode: 200,
48+
region: "my-region",
49+
url: URL(string: "https://example.com/fake_func")!,
4850
body: nil,
4951
serializer: FunctionsSerializer()
5052
)
@@ -56,6 +58,8 @@ final class FunctionsErrorTests: XCTestCase {
5658
// The error should be inferred from the HTTP status code.
5759
let error = FunctionsError(
5860
httpStatusCode: 429,
61+
region: "my-region",
62+
url: URL(string: "https://example.com/fake_func")!,
5963
body: nil,
6064
serializer: FunctionsSerializer()
6165
)
@@ -66,7 +70,7 @@ final class FunctionsErrorTests: XCTestCase {
6670
XCTAssertEqual(nsError.domain, "com.firebase.functions")
6771
XCTAssertEqual(nsError.code, 8)
6872
XCTAssertEqual(nsError.localizedDescription, "RESOURCE EXHAUSTED")
69-
XCTAssertEqual(nsError.userInfo.count, 1)
73+
XCTAssertEqual(nsError.userInfo.count, 3)
7074
}
7175

7276
func testInitWithOKStatusCodeAndIncompleteErrorBody() {
@@ -75,6 +79,8 @@ final class FunctionsErrorTests: XCTestCase {
7579

7680
let error = FunctionsError(
7781
httpStatusCode: 200,
82+
region: "my-region",
83+
url: URL(string: "https://example.com/fake_func")!,
7884
body: responseData,
7985
serializer: FunctionsSerializer()
8086
)
@@ -85,7 +91,7 @@ final class FunctionsErrorTests: XCTestCase {
8591
XCTAssertEqual(nsError.domain, "com.firebase.functions")
8692
XCTAssertEqual(nsError.code, 11)
8793
XCTAssertEqual(nsError.localizedDescription, "OUT OF RANGE")
88-
XCTAssertEqual(nsError.userInfo.count, 1)
94+
XCTAssertEqual(nsError.userInfo.count, 3)
8995
}
9096

9197
func testInitWithErrorStatusCodeAndErrorBody() {
@@ -96,6 +102,8 @@ final class FunctionsErrorTests: XCTestCase {
96102

97103
let error = FunctionsError(
98104
httpStatusCode: 499,
105+
region: "my-region",
106+
url: URL(string: "https://example.com/fake_func")!,
99107
body: responseData,
100108
serializer: FunctionsSerializer()
101109
)
@@ -106,7 +114,7 @@ final class FunctionsErrorTests: XCTestCase {
106114
XCTAssertEqual(nsError.domain, "com.firebase.functions")
107115
XCTAssertEqual(nsError.code, 11)
108116
XCTAssertEqual(nsError.localizedDescription, "TEST_ErrorMessage")
109-
XCTAssertEqual(nsError.userInfo.count, 2)
117+
XCTAssertEqual(nsError.userInfo.count, 4)
110118
XCTAssertEqual(nsError.userInfo["details"] as? Int, 123)
111119
}
112120

@@ -119,6 +127,8 @@ final class FunctionsErrorTests: XCTestCase {
119127

120128
let error = FunctionsError(
121129
httpStatusCode: 401,
130+
region: "my-region",
131+
url: URL(string: "https://example.com/fake_func")!,
122132
body: responseData,
123133
serializer: FunctionsSerializer()
124134
)
@@ -133,6 +143,8 @@ final class FunctionsErrorTests: XCTestCase {
133143

134144
let error = FunctionsError(
135145
httpStatusCode: 403,
146+
region: "my-region",
147+
url: URL(string: "https://example.com/fake_func")!,
136148
body: responseData,
137149
serializer: FunctionsSerializer()
138150
)
@@ -143,7 +155,7 @@ final class FunctionsErrorTests: XCTestCase {
143155
XCTAssertEqual(nsError.domain, "com.firebase.functions")
144156
XCTAssertEqual(nsError.code, 7) // `permissionDenied`, inferred from the HTTP status code
145157
XCTAssertEqual(nsError.localizedDescription, "TEST_ErrorMessage")
146-
XCTAssertEqual(nsError.userInfo.count, 2)
158+
XCTAssertEqual(nsError.userInfo.count, 4)
147159
XCTAssertEqual(nsError.userInfo["details"] as? NSNull, NSNull())
148160
}
149161

@@ -155,6 +167,8 @@ final class FunctionsErrorTests: XCTestCase {
155167

156168
let error = FunctionsError(
157169
httpStatusCode: 503,
170+
region: "my-region",
171+
url: URL(string: "https://example.com/fake_func")!,
158172
body: responseData,
159173
serializer: FunctionsSerializer()
160174
)

0 commit comments

Comments
 (0)