GoogleRPCStatus.swift 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. * Copyright 2024, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. public import GRPCCore
  17. public import SwiftProtobuf
  18. /// An error containing structured details which can be delivered to the client.
  19. ///
  20. /// This error follows the "richer error model" detailed in the
  21. /// [gRPC error guide](https://grpc.io/docs/guides/error/) and
  22. /// [Google AIP-193](https://google.aip.dev/193).
  23. ///
  24. /// Like an `RPCError`, this error has a `code` and `message`. However it also includes
  25. /// a list of structured error details which can be propagated to clients. A set of standard
  26. /// details are provided by ``ErrorDetails``.
  27. ///
  28. /// As a client you can extract this error from an `RPCError` using `unpackGoogleRPCStatus()`.
  29. ///
  30. /// > Implementation details:
  31. /// >
  32. /// > The error information is transmitted to clients in the trailing metadata of an RPC. It is
  33. /// > inserted into the metadata keyed by "grpc-status-details-bin". The value of the metadata is
  34. /// > the serialized bytes of a "google.protobuf.Any" protocol buffers message. The content of which
  35. /// > is a "google.rpc.Status" protocol buffers message containing the status code, message, and
  36. /// > details.
  37. public struct GoogleRPCStatus: Error {
  38. /// A code representing the high-level domain of the error.
  39. public var code: RPCError.Code
  40. /// A developer-facing error message, which should be in English.
  41. ///
  42. /// Any user-facing error message should be localized and sent in the `details` field
  43. /// or localized by the client.
  44. public var message: String
  45. /// A list of messages that carry the error details.
  46. public var details: [ErrorDetails]
  47. /// Create a new Google RPC Status error.
  48. ///
  49. /// - Parameters:
  50. /// - code: A code representing the high-level domain of the error.
  51. /// - message: A developer-facing error message.
  52. /// - details: A list of messages that carry the error details.
  53. public init(code: RPCError.Code, message: String, details: [ErrorDetails]) {
  54. self.code = code
  55. self.message = message
  56. self.details = details
  57. }
  58. /// Create a new Google RPC Status error.
  59. ///
  60. /// - Parameters:
  61. /// - code: A code representing the high-level domain of the error.
  62. /// - message: A developer-facing error message.
  63. /// - details: A list of messages that carry the error details.
  64. public init(code: RPCError.Code, message: String, details: ErrorDetails...) {
  65. self.code = code
  66. self.message = message
  67. self.details = details
  68. }
  69. }
  70. extension GoogleRPCStatus {
  71. /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` value
  72. /// containing a serialized message in Protocol Buffer binary format.
  73. ///
  74. /// - Parameters:
  75. /// - bytes: The binary-encoded message data to decode.
  76. /// - extensions: An `ExtensionMap` used to look up and decode any
  77. /// extensions in this message or messages nested within this message's
  78. /// fields.
  79. /// - partial: If `false` (the default), this method will check if the `Message`
  80. /// is initialized after decoding to verify that all required fields are present.
  81. /// If any are missing, this method throws `BinaryDecodingError`.
  82. /// - options: The `BinaryDecodingOptions` to use.
  83. /// - Throws: `BinaryDecodingError` if decoding fails.
  84. public init<Bytes: SwiftProtobufContiguousBytes>(
  85. serializedBytes bytes: Bytes,
  86. extensions: (any ExtensionMap)? = nil,
  87. partial: Bool = false,
  88. options: BinaryDecodingOptions = BinaryDecodingOptions()
  89. ) throws {
  90. let status = try Google_Rpc_Status(
  91. serializedBytes: bytes,
  92. extensions: extensions,
  93. partial: partial,
  94. options: options
  95. )
  96. let statusCode = Status.Code(rawValue: Int(status.code))
  97. self.code = statusCode.flatMap { RPCError.Code($0) } ?? .unknown
  98. self.message = status.message
  99. self.details = try status.details.map { try ErrorDetails(unpacking: $0) }
  100. }
  101. /// Returns a `SwiftProtobufContiguousBytes` instance containing the Protocol Buffer binary
  102. /// format serialization of the message.
  103. ///
  104. /// - Parameters:
  105. /// - partial: If `false` (the default), this method will check
  106. /// `Message.isInitialized` before encoding to verify that all required
  107. /// fields are present. If any are missing, this method throws.
  108. /// `BinaryEncodingError/missingRequiredFields`.
  109. /// - options: The `BinaryEncodingOptions` to use.
  110. /// - Returns: A `SwiftProtobufContiguousBytes` instance containing the binary serialization
  111. /// of the message.
  112. ///
  113. /// - Throws: `SwiftProtobufError` or `BinaryEncodingError` if encoding fails.
  114. public func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(
  115. partial: Bool = false,
  116. options: BinaryEncodingOptions = BinaryEncodingOptions()
  117. ) throws -> Bytes {
  118. let status = try Google_Rpc_Status.with {
  119. $0.code = Int32(self.code.rawValue)
  120. $0.message = self.message
  121. $0.details = try self.details.map { try $0.pack() }
  122. }
  123. return try status.serializedBytes(partial: partial, options: options)
  124. }
  125. }
  126. extension GoogleRPCStatus: RPCErrorConvertible {
  127. public var rpcErrorCode: RPCError.Code { self.code }
  128. public var rpcErrorMessage: String { self.message }
  129. public var rpcErrorMetadata: Metadata {
  130. do {
  131. let bytes: [UInt8] = try self.serializedBytes()
  132. return [Metadata.statusDetailsBinKey: .binary(bytes)]
  133. } catch {
  134. // Failed to serialize error details. Not a lot can be done here.
  135. return [:]
  136. }
  137. }
  138. }