| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- /*
- * Copyright 2023, gRPC Authors All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /// Configuration values for executing an RPC.
- ///
- /// See also: https://github.com/grpc/grpc-proto/blob/master/grpc/service_config/service_config.proto
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- public struct MethodConfiguration: Hashable, Sendable {
- public struct Name: Sendable, Hashable {
- /// The name of the service, including the namespace.
- ///
- /// If the service is empty then `method` must also be empty and the configuration specifies
- /// defaults for all methods.
- ///
- /// - Precondition: If `service` is empty then `method` must also be empty.
- public var service: String {
- didSet { try! self.validate() }
- }
- /// The name of the method.
- ///
- /// If the method is empty then the configuration will be the default for all methods in the
- /// specified service.
- public var method: String
- /// Create a new name.
- ///
- /// If the service is empty then `method` must also be empty and the configuration specifies
- /// defaults for all methods. If only `method` is empty then the configuration applies to
- /// all methods in the `service`.
- ///
- /// - Parameters:
- /// - service: The name of the service, including the namespace.
- /// - method: The name of the method.
- public init(service: String, method: String = "") {
- self.service = service
- self.method = method
- try! self.validate()
- }
- private func validate() throws {
- if self.service.isEmpty && !self.method.isEmpty {
- throw RuntimeError(
- code: .invalidArgument,
- message: "'method' must be empty if 'service' is empty."
- )
- }
- }
- }
- /// The names of methods which this configuration applies to.
- public var names: [Name]
- /// Whether RPCs for this method should wait until the connection is ready.
- ///
- /// If `false` the RPC will abort immediately if there is a transient failure connecting to
- /// the server. Otherwise gRPC will attempt to connect until the deadline is exceeded.
- public var waitForReady: Bool?
- /// The default timeout for the RPC.
- ///
- /// If no reply is received in the specified amount of time the request is aborted
- /// with an ``RPCError`` with code ``RPCError/Code/deadlineExceeded``.
- ///
- /// The actual deadline used will be the minimum of the value specified here
- /// and the value set by the application by the client API. If either one isn't set
- /// then the other value is used. If neither is set then the request has no deadline.
- ///
- /// The timeout applies to the overall execution of an RPC. If, for example, a retry
- /// policy is set then the timeout begins when the first attempt is started and _isn't_ reset
- /// when subsequent attempts start.
- public var timeout: Duration?
- /// The maximum allowed payload size in bytes for an individual message.
- ///
- /// If a client attempts to send an object larger than this value, it will not be sent and the
- /// client will see an error. Note that 0 is a valid value, meaning that the request message
- /// must be empty.
- public var maxRequestMessageBytes: Int?
- /// The maximum allowed payload size in bytes for an individual response message.
- ///
- /// If a server attempts to send an object larger than this value, it will not
- /// be sent, and an error will be sent to the client instead. Note that 0 is a valid value,
- /// meaning that the response message must be empty.
- public var maxResponseMessageBytes: Int?
- /// The policy determining how many times, and when, the RPC is executed.
- ///
- /// There are two policy types:
- /// 1. Retry
- /// 2. Hedging
- ///
- /// The retry policy allows an RPC to be retried a limited number of times if the RPC
- /// fails with one of the configured set of status codes. RPCs are only retried if they
- /// fail immediately, that is, the first response part received from the server is a
- /// status code.
- ///
- /// The hedging policy allows an RPC to be executed multiple times concurrently. Typically
- /// each execution will be staggered by some delay. The first successful response will be
- /// reported to the client. Hedging is only suitable for idempotent RPCs.
- public var executionPolicy: ExecutionPolicy?
- /// Create an execution configuration.
- ///
- /// - Parameters:
- /// - names: The names of methods this configuration applies to.
- /// - waitForReady: Whether RPCs sent to this method should wait until the connection is ready.
- /// - timeout: The default timeout for the RPC.
- /// - maxRequestMessageBytes: The maximum allowed size of a request message in bytes.
- /// - maxResponseMessageBytes: The maximum allowed size of a response message in bytes.
- /// - executionPolicy: The execution policy to use for the RPC.
- public init(
- names: [Name],
- waitForReady: Bool? = nil,
- timeout: Duration? = nil,
- maxRequestMessageBytes: Int? = nil,
- maxResponseMessageBytes: Int? = nil,
- executionPolicy: ExecutionPolicy? = nil
- ) {
- self.names = names
- self.waitForReady = waitForReady
- self.timeout = timeout
- self.maxRequestMessageBytes = maxRequestMessageBytes
- self.maxResponseMessageBytes = maxResponseMessageBytes
- self.executionPolicy = executionPolicy
- }
- }
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- extension MethodConfiguration {
- /// The execution policy for an RPC.
- public enum ExecutionPolicy: Hashable, Sendable {
- /// Policy for retrying an RPC.
- ///
- /// See ``RetryPolicy`` for more details.
- case retry(RetryPolicy)
- /// Policy for hedging an RPC.
- ///
- /// See ``HedgingPolicy`` for more details.
- case hedge(HedgingPolicy)
- }
- }
- /// Policy for retrying an RPC.
- ///
- /// gRPC retries RPCs when the first response from the server is a status code which matches
- /// one of the configured retryable status codes. If the server begins processing the RPC and
- /// first responds with metadata and later responds with a retryable status code then the RPC
- /// won't be retried.
- ///
- /// Execution attempts are limited by ``maximumAttempts`` which includes the original attempt. The
- /// maximum number of attempts is limited to five.
- ///
- /// Subsequent attempts are executed after some delay. The first _retry_, or second attempt, will
- /// be started after a randomly chosen delay between zero and ``initialBackoff``. More generally,
- /// the nth retry will happen after a randomly chosen delay between zero
- /// and `min(initialBackoff * backoffMultiplier^(n-1), maximumBackoff)`.
- ///
- /// For more information see [gRFC A6 Client
- /// Retries](https://github.com/grpc/proposal/blob/master/A6-client-retries.md).
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- public struct RetryPolicy: Hashable, Sendable {
- /// The maximum number of RPC attempts, including the original attempt.
- ///
- /// Must be greater than one, values greater than five are treated as five.
- public var maximumAttempts: Int {
- didSet { self.maximumAttempts = try! validateMaxAttempts(self.maximumAttempts) }
- }
- /// The initial backoff duration.
- ///
- /// The initial retry will occur after a random amount of time up to this value.
- ///
- /// - Precondition: Must be greater than zero.
- public var initialBackoff: Duration {
- willSet { try! Self.validateInitialBackoff(newValue) }
- }
- /// The maximum amount of time to backoff for.
- ///
- /// - Precondition: Must be greater than zero.
- public var maximumBackoff: Duration {
- willSet { try! Self.validateMaxBackoff(newValue) }
- }
- /// The multiplier to apply to backoff.
- ///
- /// - Precondition: Must be greater than zero.
- public var backoffMultiplier: Double {
- willSet { try! Self.validateBackoffMultiplier(newValue) }
- }
- /// The set of status codes which may be retried.
- ///
- /// - Precondition: Must not be empty.
- public var retryableStatusCodes: Set<Status.Code> {
- willSet { try! Self.validateRetryableStatusCodes(newValue) }
- }
- /// Create a new retry policy.
- ///
- /// - Parameters:
- /// - maximumAttempts: The maximum number of attempts allowed for the RPC.
- /// - initialBackoff: The initial backoff period for the first retry attempt. Must be
- /// greater than zero.
- /// - maximumBackoff: The maximum period of time to wait between attempts. Must be greater than
- /// zero.
- /// - backoffMultiplier: The exponential backoff multiplier. Must be greater than zero.
- /// - retryableStatusCodes: The set of status codes which may be retried. Must not be empty.
- /// - Precondition: `maximumAttempts`, `initialBackoff`, `maximumBackoff` and `backoffMultiplier`
- /// must be greater than zero.
- /// - Precondition: `retryableStatusCodes` must not be empty.
- public init(
- maximumAttempts: Int,
- initialBackoff: Duration,
- maximumBackoff: Duration,
- backoffMultiplier: Double,
- retryableStatusCodes: Set<Status.Code>
- ) {
- self.maximumAttempts = try! validateMaxAttempts(maximumAttempts)
- try! Self.validateInitialBackoff(initialBackoff)
- self.initialBackoff = initialBackoff
- try! Self.validateMaxBackoff(maximumBackoff)
- self.maximumBackoff = maximumBackoff
- try! Self.validateBackoffMultiplier(backoffMultiplier)
- self.backoffMultiplier = backoffMultiplier
- try! Self.validateRetryableStatusCodes(retryableStatusCodes)
- self.retryableStatusCodes = retryableStatusCodes
- }
- private static func validateInitialBackoff(_ value: Duration) throws {
- if value <= .zero {
- throw RuntimeError(
- code: .invalidArgument,
- message: "initialBackoff must be greater than zero"
- )
- }
- }
- private static func validateMaxBackoff(_ value: Duration) throws {
- if value <= .zero {
- throw RuntimeError(
- code: .invalidArgument,
- message: "maximumBackoff must be greater than zero"
- )
- }
- }
- private static func validateBackoffMultiplier(_ value: Double) throws {
- if value <= 0 {
- throw RuntimeError(
- code: .invalidArgument,
- message: "backoffMultiplier must be greater than zero"
- )
- }
- }
- private static func validateRetryableStatusCodes(_ value: Set<Status.Code>) throws {
- if value.isEmpty {
- throw RuntimeError(code: .invalidArgument, message: "retryableStatusCodes mustn't be empty")
- }
- }
- }
- /// Policy for hedging an RPC.
- ///
- /// Hedged RPCs may execute more than once on a server so only idempotent methods should
- /// be hedged.
- ///
- /// gRPC executes the RPC at most ``maximumAttempts`` times, staggering each attempt
- /// by ``hedgingDelay``.
- ///
- /// For more information see [gRFC A6 Client
- /// Retries](https://github.com/grpc/proposal/blob/master/A6-client-retries.md).
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- public struct HedgingPolicy: Hashable, Sendable {
- /// The maximum number of RPC attempts, including the original attempt.
- ///
- /// Values greater than five are treated as five.
- ///
- /// - Precondition: Must be greater than one.
- public var maximumAttempts: Int {
- didSet { self.maximumAttempts = try! validateMaxAttempts(self.maximumAttempts) }
- }
- /// The first RPC will be sent immediately, but each subsequent RPC will be sent at intervals
- /// of `hedgingDelay`. Set this to zero to immediately send all RPCs.
- public var hedgingDelay: Duration {
- willSet { try! Self.validateHedgingDelay(newValue) }
- }
- /// The set of status codes which indicate other hedged RPCs may still succeed.
- ///
- /// If a non-fatal status code is returned by the server, hedged RPCs will continue.
- /// Otherwise, outstanding requests will be cancelled and the error returned to the
- /// application layer.
- public var nonFatalStatusCodes: Set<Status.Code>
- /// Create a new hedging policy.
- ///
- /// - Parameters:
- /// - maximumAttempts: The maximum number of attempts allowed for the RPC.
- /// - hedgingDelay: The delay between each hedged RPC.
- /// - nonFatalStatusCodes: The set of status codes which indicate other hedged RPCs may still
- /// succeed.
- /// - Precondition: `maximumAttempts` must be greater than zero.
- public init(
- maximumAttempts: Int,
- hedgingDelay: Duration,
- nonFatalStatusCodes: Set<Status.Code>
- ) {
- self.maximumAttempts = try! validateMaxAttempts(maximumAttempts)
- try! Self.validateHedgingDelay(hedgingDelay)
- self.hedgingDelay = hedgingDelay
- self.nonFatalStatusCodes = nonFatalStatusCodes
- }
- private static func validateHedgingDelay(_ value: Duration) throws {
- if value < .zero {
- throw RuntimeError(
- code: .invalidArgument,
- message: "hedgingDelay must be greater than or equal to zero"
- )
- }
- }
- }
- private func validateMaxAttempts(_ value: Int) throws -> Int {
- guard value > 1 else {
- throw RuntimeError(
- code: .invalidArgument,
- message: "max_attempts must be greater than one (was \(value))"
- )
- }
- return min(value, 5)
- }
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- extension Duration {
- fileprivate init(googleProtobufDuration duration: String) throws {
- guard duration.utf8.last == UInt8(ascii: "s"),
- let fractionalSeconds = Double(duration.dropLast())
- else {
- throw RuntimeError(code: .invalidArgument, message: "Invalid google.protobuf.duration")
- }
- let seconds = fractionalSeconds.rounded(.down)
- let attoseconds = (fractionalSeconds - seconds) / 1e18
- self.init(secondsComponent: Int64(seconds), attosecondsComponent: Int64(attoseconds))
- }
- }
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- extension MethodConfiguration: Codable {
- private enum CodingKeys: String, CodingKey {
- case name
- case waitForReady
- case timeout
- case maxRequestMessageBytes
- case maxResponseMessageBytes
- case retryPolicy
- case hedgingPolicy
- }
- public init(from decoder: any Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- self.names = try container.decode([Name].self, forKey: .name)
- let waitForReady = try container.decodeIfPresent(Bool.self, forKey: .waitForReady)
- self.waitForReady = waitForReady
- let timeout = try container.decodeIfPresent(GoogleProtobufDuration.self, forKey: .timeout)
- self.timeout = timeout?.duration
- let maxRequestSize = try container.decodeIfPresent(Int.self, forKey: .maxRequestMessageBytes)
- self.maxRequestMessageBytes = maxRequestSize
- let maxResponseSize = try container.decodeIfPresent(Int.self, forKey: .maxResponseMessageBytes)
- self.maxResponseMessageBytes = maxResponseSize
- if let policy = try container.decodeIfPresent(HedgingPolicy.self, forKey: .hedgingPolicy) {
- self.executionPolicy = .hedge(policy)
- } else if let policy = try container.decodeIfPresent(RetryPolicy.self, forKey: .retryPolicy) {
- self.executionPolicy = .retry(policy)
- } else {
- self.executionPolicy = nil
- }
- }
- public func encode(to encoder: any Encoder) throws {
- var container = encoder.container(keyedBy: CodingKeys.self)
- try container.encode(self.names, forKey: .name)
- try container.encodeIfPresent(
- self.timeout.map { GoogleProtobufDuration(duration: $0) },
- forKey: .timeout
- )
- try container.encodeIfPresent(self.maxRequestMessageBytes, forKey: .maxRequestMessageBytes)
- try container.encodeIfPresent(self.maxResponseMessageBytes, forKey: .maxResponseMessageBytes)
- switch self.executionPolicy {
- case .retry(let policy):
- try container.encode(policy, forKey: .retryPolicy)
- case .hedge(let policy):
- try container.encode(policy, forKey: .hedgingPolicy)
- case .none:
- ()
- }
- }
- }
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- extension MethodConfiguration.Name: Codable {
- private enum CodingKeys: String, CodingKey {
- case service
- case method
- }
- public init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- let service = try container.decodeIfPresent(String.self, forKey: .service)
- self.service = service ?? ""
- let method = try container.decodeIfPresent(String.self, forKey: .method)
- self.method = method ?? ""
- try self.validate()
- }
- public func encode(to encoder: any Encoder) throws {
- var container = encoder.container(keyedBy: CodingKeys.self)
- try container.encode(self.method, forKey: .method)
- try container.encode(self.service, forKey: .service)
- }
- }
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- extension RetryPolicy: Codable {
- private enum CodingKeys: String, CodingKey {
- case maxAttempts
- case initialBackoff
- case maxBackoff
- case backoffMultiplier
- case retryableStatusCodes
- }
- public init(from decoder: any Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- let maxAttempts = try container.decode(Int.self, forKey: .maxAttempts)
- self.maximumAttempts = try validateMaxAttempts(maxAttempts)
- let initialBackoff = try container.decode(String.self, forKey: .initialBackoff)
- self.initialBackoff = try Duration(googleProtobufDuration: initialBackoff)
- try Self.validateInitialBackoff(self.initialBackoff)
- let maxBackoff = try container.decode(String.self, forKey: .maxBackoff)
- self.maximumBackoff = try Duration(googleProtobufDuration: maxBackoff)
- try Self.validateMaxBackoff(self.maximumBackoff)
- self.backoffMultiplier = try container.decode(Double.self, forKey: .backoffMultiplier)
- try Self.validateBackoffMultiplier(self.backoffMultiplier)
- let codes = try container.decode([GoogleRPCCode].self, forKey: .retryableStatusCodes)
- self.retryableStatusCodes = Set(codes.map { $0.code })
- try Self.validateRetryableStatusCodes(self.retryableStatusCodes)
- }
- public func encode(to encoder: any Encoder) throws {
- var container = encoder.container(keyedBy: CodingKeys.self)
- try container.encode(self.maximumAttempts, forKey: .maxAttempts)
- try container.encode(
- GoogleProtobufDuration(duration: self.initialBackoff),
- forKey: .initialBackoff
- )
- try container.encode(GoogleProtobufDuration(duration: self.maximumBackoff), forKey: .maxBackoff)
- try container.encode(self.backoffMultiplier, forKey: .backoffMultiplier)
- try container.encode(
- self.retryableStatusCodes.map { $0.googleRPCCode },
- forKey: .retryableStatusCodes
- )
- }
- }
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- extension HedgingPolicy: Codable {
- private enum CodingKeys: String, CodingKey {
- case maxAttempts
- case hedgingDelay
- case nonFatalStatusCodes
- }
- public init(from decoder: any Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- let maxAttempts = try container.decode(Int.self, forKey: .maxAttempts)
- self.maximumAttempts = try validateMaxAttempts(maxAttempts)
- let delay = try container.decode(String.self, forKey: .hedgingDelay)
- self.hedgingDelay = try Duration(googleProtobufDuration: delay)
- let statusCodes = try container.decode([GoogleRPCCode].self, forKey: .nonFatalStatusCodes)
- self.nonFatalStatusCodes = Set(statusCodes.map { $0.code })
- }
- public func encode(to encoder: any Encoder) throws {
- var container = encoder.container(keyedBy: CodingKeys.self)
- try container.encode(self.maximumAttempts, forKey: .maxAttempts)
- try container.encode(GoogleProtobufDuration(duration: self.hedgingDelay), forKey: .hedgingDelay)
- try container.encode(
- self.nonFatalStatusCodes.map { $0.googleRPCCode },
- forKey: .nonFatalStatusCodes
- )
- }
- }
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct GoogleProtobufDuration: Codable {
- var duration: Duration
- init(duration: Duration) {
- self.duration = duration
- }
- init(from decoder: any Decoder) throws {
- let container = try decoder.singleValueContainer()
- let duration = try container.decode(String.self)
- guard duration.utf8.last == UInt8(ascii: "s"),
- let fractionalSeconds = Double(duration.dropLast())
- else {
- throw RuntimeError(code: .invalidArgument, message: "Invalid google.protobuf.duration")
- }
- let seconds = fractionalSeconds.rounded(.down)
- let attoseconds = (fractionalSeconds - seconds) * 1e18
- self.duration = Duration(
- secondsComponent: Int64(seconds),
- attosecondsComponent: Int64(attoseconds)
- )
- }
- func encode(to encoder: any Encoder) throws {
- var container = encoder.singleValueContainer()
- var seconds = Double(self.duration.components.seconds)
- seconds += Double(self.duration.components.attoseconds) / 1e18
- let durationString = "\(seconds)s"
- try container.encode(durationString)
- }
- }
- struct GoogleRPCCode: Codable {
- var code: Status.Code
- init(code: Status.Code) {
- self.code = code
- }
- init(from decoder: Decoder) throws {
- let container = try decoder.singleValueContainer()
- let code: Status.Code?
- if let caseName = try? container.decode(String.self) {
- code = Status.Code(googleRPCCode: caseName)
- } else if let rawValue = try? container.decode(Int.self) {
- code = Status.Code(rawValue: rawValue)
- } else {
- code = nil
- }
- if let code = code {
- self.code = code
- } else {
- throw RuntimeError(code: .invalidArgument, message: "Invalid google.rpc.code")
- }
- }
- func encode(to encoder: Encoder) throws {
- var container = encoder.singleValueContainer()
- try container.encode(self.code.googleRPCCode)
- }
- }
- extension Status.Code {
- fileprivate init?(googleRPCCode code: String) {
- switch code {
- case "OK":
- self = .ok
- case "CANCELLED":
- self = .cancelled
- case "UNKNOWN":
- self = .unknown
- case "INVALID_ARGUMENT":
- self = .invalidArgument
- case "DEADLINE_EXCEEDED":
- self = .deadlineExceeded
- case "NOT_FOUND":
- self = .notFound
- case "ALREADY_EXISTS":
- self = .alreadyExists
- case "PERMISSION_DENIED":
- self = .permissionDenied
- case "RESOURCE_EXHAUSTED":
- self = .resourceExhausted
- case "FAILED_PRECONDITION":
- self = .failedPrecondition
- case "ABORTED":
- self = .aborted
- case "OUT_OF_RANGE":
- self = .outOfRange
- case "UNIMPLEMENTED":
- self = .unimplemented
- case "INTERNAL":
- self = .internalError
- case "UNAVAILABLE":
- self = .unavailable
- case "DATA_LOSS":
- self = .dataLoss
- case "UNAUTHENTICATED":
- self = .unauthenticated
- default:
- return nil
- }
- }
- fileprivate var googleRPCCode: String {
- switch self.wrapped {
- case .ok:
- return "OK"
- case .cancelled:
- return "CANCELLED"
- case .unknown:
- return "UNKNOWN"
- case .invalidArgument:
- return "INVALID_ARGUMENT"
- case .deadlineExceeded:
- return "DEADLINE_EXCEEDED"
- case .notFound:
- return "NOT_FOUND"
- case .alreadyExists:
- return "ALREADY_EXISTS"
- case .permissionDenied:
- return "PERMISSION_DENIED"
- case .resourceExhausted:
- return "RESOURCE_EXHAUSTED"
- case .failedPrecondition:
- return "FAILED_PRECONDITION"
- case .aborted:
- return "ABORTED"
- case .outOfRange:
- return "OUT_OF_RANGE"
- case .unimplemented:
- return "UNIMPLEMENTED"
- case .internalError:
- return "INTERNAL"
- case .unavailable:
- return "UNAVAILABLE"
- case .dataLoss:
- return "DATA_LOSS"
- case .unauthenticated:
- return "UNAUTHENTICATED"
- }
- }
- }
|