Yakov Manshin 1 жил өмнө
parent
commit
7700b58836

+ 2 - 2
FirebaseFunctions/Sources/Functions.swift

@@ -509,7 +509,7 @@ enum FunctionsConstants {
     if let error = error as NSError? {
       let localError: (any Error)?
       if error.domain == kGTMSessionFetcherStatusDomain {
-        localError = FunctionsErrorForResponse(
+        localError = FunctionsErrorCode.errorForResponse(
           status: error.code,
           body: data,
           serializer: serializer
@@ -529,7 +529,7 @@ enum FunctionsConstants {
     }
 
     // Case 3: `data` is not `nil` but might specify a custom error -> throws conditionally
-    if let bodyError = FunctionsErrorForResponse(
+    if let bodyError = FunctionsErrorCode.errorForResponse(
       status: 200,
       body: data,
       serializer: serializer

+ 63 - 74
FirebaseFunctions/Sources/FunctionsError.swift

@@ -101,45 +101,32 @@ public let FunctionsErrorDetailsKey: String = "details"
   case unauthenticated = 16
 }
 
-/**
- * Takes an HTTP status code and returns the corresponding `FIRFunctionsErrorCode` error code.
- * This is the standard HTTP status code -> error mapping defined in:
- * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
- * - Parameter status An HTTP status code.
- * - Returns: The corresponding error code, or `FIRFunctionsErrorCodeUnknown` if none.
- */
-func FunctionsCodeForHTTPStatus(_ status: NSInteger) -> FunctionsErrorCode {
-  switch status {
-  case 200:
-    return .OK
-  case 400:
-    return .invalidArgument
-  case 401:
-    return .unauthenticated
-  case 403:
-    return .permissionDenied
-  case 404:
-    return .notFound
-  case 409:
-    return .alreadyExists
-  case 429:
-    return .resourceExhausted
-  case 499:
-    return .cancelled
-  case 500:
-    return .internal
-  case 501:
-    return .unimplemented
-  case 503:
-    return .unavailable
-  case 504:
-    return .deadlineExceeded
-  default:
-    return .internal
+extension FunctionsErrorCode {
+  /// Takes an HTTP status code and returns the corresponding `FIRFunctionsErrorCode` error code.
+  ///
+  /// + This is the standard HTTP status code -> error mapping defined in:
+  /// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
+  ///
+  /// - Parameter status: An HTTP status code.
+  /// - Returns: A `FunctionsErrorCode`. Falls back to `internal` for unknown status codes.
+  static func errorCode(forHTTPStatus status: Int) -> Self {
+    switch status {
+    case 200: .OK
+    case 400: .invalidArgument
+    case 401: .unauthenticated
+    case 403: .permissionDenied
+    case 404: .notFound
+    case 409: .alreadyExists
+    case 429: .resourceExhausted
+    case 499: .cancelled
+    case 500: .internal
+    case 501: .unimplemented
+    case 503: .unavailable
+    case 504: .deadlineExceeded
+    default: .internal
+    }
   }
-}
 
-extension FunctionsErrorCode {
   static func errorCode(forName name: String) -> FunctionsErrorCode {
     switch name {
     case "OK": return .OK
@@ -207,53 +194,55 @@ extension FunctionsErrorCode {
                    code: rawValue,
                    userInfo: userInfo ?? [NSLocalizedDescriptionKey: descriptionForErrorCode])
   }
-}
 
-func FunctionsErrorForResponse(status: NSInteger,
+  static func errorForResponse(status: Int,
                                body: Data?,
                                serializer: FunctionsSerializer) -> NSError? {
-  // Start with reasonable defaults from the status code.
-  var code = FunctionsCodeForHTTPStatus(status)
-  var description = code.descriptionForErrorCode
-
-  var details: AnyObject?
+    // Start with reasonable defaults from the status code.
+    var code = FunctionsErrorCode.errorCode(forHTTPStatus: status)
+    var description = code.descriptionForErrorCode
+    var details: AnyObject?
+
+    // Then look through the body for explicit details.
+    if let body,
+       let json = try? JSONSerialization.jsonObject(with: body) as? NSDictionary,
+       let errorDetails = json["error"] as? NSDictionary {
+      if let status = errorDetails["status"] as? String {
+        code = .errorCode(forName: status)
+
+        // If the code in the body is invalid, treat the whole response as malformed.
+        guard code != .internal else {
+          return code.generatedError(userInfo: nil)
+        }
+      }
 
-  // Then look through the body for explicit details.
-  if let body,
-     let json = try? JSONSerialization.jsonObject(with: body) as? NSDictionary,
-     let errorDetails = json["error"] as? NSDictionary {
-    if let status = errorDetails["status"] as? String {
-      code = FunctionsErrorCode.errorCode(forName: status)
+      if let message = errorDetails["message"] as? String {
+        description = message
+      } else {
+        description = code.descriptionForErrorCode
+      }
 
-      // If the code in the body is invalid, treat the whole response as malformed.
-      guard code != .internal else {
-        return code.generatedError(userInfo: nil)
+      details = errorDetails["details"] as AnyObject?
+      // Update `details` only if decoding succeeds;
+      // otherwise, keep the original object.
+      if let innerDetails = details,
+         let decodedDetails = try? serializer.decode(innerDetails) {
+        details = decodedDetails
       }
     }
 
-    if let message = errorDetails["message"] as? String {
-      description = message
-    } else {
-      description = code.descriptionForErrorCode
+    if code == .OK {
+      // Technically, there's an edge case where a developer could explicitly return an error code
+      // of
+      // OK, and we will treat it as success, but that seems reasonable.
+      return nil
     }
 
-    details = errorDetails["details"] as AnyObject?
-    if let innerDetails = details {
-      // Just ignore the details if there an error decoding them.
-      details = try? serializer.decode(innerDetails)
+    var userInfo = [String: Any]()
+    userInfo[NSLocalizedDescriptionKey] = description
+    if let details {
+      userInfo[FunctionsErrorDetailsKey] = details
     }
+    return code.generatedError(userInfo: userInfo)
   }
-
-  if code == .OK {
-    // Technically, there's an edge case where a developer could explicitly return an error code of
-    // OK, and we will treat it as success, but that seems reasonable.
-    return nil
-  }
-
-  var userInfo = [String: Any]()
-  userInfo[NSLocalizedDescriptionKey] = description
-  if let details {
-    userInfo[FunctionsErrorDetailsKey] = details
-  }
-  return code.generatedError(userInfo: userInfo)
 }