Skip to content

Commit 7700b58

Browse files
authored
Updated Functions Errors (#13535)
1 parent ce0baa2 commit 7700b58

File tree

2 files changed

+65
-76
lines changed

2 files changed

+65
-76
lines changed

FirebaseFunctions/Sources/Functions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ enum FunctionsConstants {
509509
if let error = error as NSError? {
510510
let localError: (any Error)?
511511
if error.domain == kGTMSessionFetcherStatusDomain {
512-
localError = FunctionsErrorForResponse(
512+
localError = FunctionsErrorCode.errorForResponse(
513513
status: error.code,
514514
body: data,
515515
serializer: serializer
@@ -529,7 +529,7 @@ enum FunctionsConstants {
529529
}
530530

531531
// Case 3: `data` is not `nil` but might specify a custom error -> throws conditionally
532-
if let bodyError = FunctionsErrorForResponse(
532+
if let bodyError = FunctionsErrorCode.errorForResponse(
533533
status: 200,
534534
body: data,
535535
serializer: serializer

FirebaseFunctions/Sources/FunctionsError.swift

Lines changed: 63 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -101,45 +101,32 @@ public let FunctionsErrorDetailsKey: String = "details"
101101
case unauthenticated = 16
102102
}
103103

104-
/**
105-
* Takes an HTTP status code and returns the corresponding `FIRFunctionsErrorCode` error code.
106-
* This is the standard HTTP status code -> error mapping defined in:
107-
* https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
108-
* - Parameter status An HTTP status code.
109-
* - Returns: The corresponding error code, or `FIRFunctionsErrorCodeUnknown` if none.
110-
*/
111-
func FunctionsCodeForHTTPStatus(_ status: NSInteger) -> FunctionsErrorCode {
112-
switch status {
113-
case 200:
114-
return .OK
115-
case 400:
116-
return .invalidArgument
117-
case 401:
118-
return .unauthenticated
119-
case 403:
120-
return .permissionDenied
121-
case 404:
122-
return .notFound
123-
case 409:
124-
return .alreadyExists
125-
case 429:
126-
return .resourceExhausted
127-
case 499:
128-
return .cancelled
129-
case 500:
130-
return .internal
131-
case 501:
132-
return .unimplemented
133-
case 503:
134-
return .unavailable
135-
case 504:
136-
return .deadlineExceeded
137-
default:
138-
return .internal
104+
extension FunctionsErrorCode {
105+
/// Takes an HTTP status code and returns the corresponding `FIRFunctionsErrorCode` error code.
106+
///
107+
/// + This is the standard HTTP status code -> error mapping defined in:
108+
/// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
109+
///
110+
/// - Parameter status: An HTTP status code.
111+
/// - Returns: A `FunctionsErrorCode`. Falls back to `internal` for unknown status codes.
112+
static func errorCode(forHTTPStatus status: Int) -> Self {
113+
switch status {
114+
case 200: .OK
115+
case 400: .invalidArgument
116+
case 401: .unauthenticated
117+
case 403: .permissionDenied
118+
case 404: .notFound
119+
case 409: .alreadyExists
120+
case 429: .resourceExhausted
121+
case 499: .cancelled
122+
case 500: .internal
123+
case 501: .unimplemented
124+
case 503: .unavailable
125+
case 504: .deadlineExceeded
126+
default: .internal
127+
}
139128
}
140-
}
141129

142-
extension FunctionsErrorCode {
143130
static func errorCode(forName name: String) -> FunctionsErrorCode {
144131
switch name {
145132
case "OK": return .OK
@@ -207,53 +194,55 @@ extension FunctionsErrorCode {
207194
code: rawValue,
208195
userInfo: userInfo ?? [NSLocalizedDescriptionKey: descriptionForErrorCode])
209196
}
210-
}
211197

212-
func FunctionsErrorForResponse(status: NSInteger,
198+
static func errorForResponse(status: Int,
213199
body: Data?,
214200
serializer: FunctionsSerializer) -> NSError? {
215-
// Start with reasonable defaults from the status code.
216-
var code = FunctionsCodeForHTTPStatus(status)
217-
var description = code.descriptionForErrorCode
218-
219-
var details: AnyObject?
201+
// Start with reasonable defaults from the status code.
202+
var code = FunctionsErrorCode.errorCode(forHTTPStatus: status)
203+
var description = code.descriptionForErrorCode
204+
var details: AnyObject?
205+
206+
// Then look through the body for explicit details.
207+
if let body,
208+
let json = try? JSONSerialization.jsonObject(with: body) as? NSDictionary,
209+
let errorDetails = json["error"] as? NSDictionary {
210+
if let status = errorDetails["status"] as? String {
211+
code = .errorCode(forName: status)
212+
213+
// If the code in the body is invalid, treat the whole response as malformed.
214+
guard code != .internal else {
215+
return code.generatedError(userInfo: nil)
216+
}
217+
}
220218

221-
// Then look through the body for explicit details.
222-
if let body,
223-
let json = try? JSONSerialization.jsonObject(with: body) as? NSDictionary,
224-
let errorDetails = json["error"] as? NSDictionary {
225-
if let status = errorDetails["status"] as? String {
226-
code = FunctionsErrorCode.errorCode(forName: status)
219+
if let message = errorDetails["message"] as? String {
220+
description = message
221+
} else {
222+
description = code.descriptionForErrorCode
223+
}
227224

228-
// If the code in the body is invalid, treat the whole response as malformed.
229-
guard code != .internal else {
230-
return code.generatedError(userInfo: nil)
225+
details = errorDetails["details"] as AnyObject?
226+
// Update `details` only if decoding succeeds;
227+
// otherwise, keep the original object.
228+
if let innerDetails = details,
229+
let decodedDetails = try? serializer.decode(innerDetails) {
230+
details = decodedDetails
231231
}
232232
}
233233

234-
if let message = errorDetails["message"] as? String {
235-
description = message
236-
} else {
237-
description = code.descriptionForErrorCode
234+
if code == .OK {
235+
// Technically, there's an edge case where a developer could explicitly return an error code
236+
// of
237+
// OK, and we will treat it as success, but that seems reasonable.
238+
return nil
238239
}
239240

240-
details = errorDetails["details"] as AnyObject?
241-
if let innerDetails = details {
242-
// Just ignore the details if there an error decoding them.
243-
details = try? serializer.decode(innerDetails)
241+
var userInfo = [String: Any]()
242+
userInfo[NSLocalizedDescriptionKey] = description
243+
if let details {
244+
userInfo[FunctionsErrorDetailsKey] = details
244245
}
246+
return code.generatedError(userInfo: userInfo)
245247
}
246-
247-
if code == .OK {
248-
// Technically, there's an edge case where a developer could explicitly return an error code of
249-
// OK, and we will treat it as success, but that seems reasonable.
250-
return nil
251-
}
252-
253-
var userInfo = [String: Any]()
254-
userInfo[NSLocalizedDescriptionKey] = description
255-
if let details {
256-
userInfo[FunctionsErrorDetailsKey] = details
257-
}
258-
return code.generatedError(userInfo: userInfo)
259248
}

0 commit comments

Comments
 (0)