diff --git a/.swiftlint.yml b/.swiftlint.yml index 6361f435c..d2b385531 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,13 +2,7 @@ excluded: - .build - Sources/SmithyTestUtil/* - Sources/WeatherSDK/* - - Tests/ClientRuntimeTests/* - - Tests/SmithyTestUtilTests/* - - Tests/SmithyXMLTests/* - - Tests/SmithyJSONTests/* - - Tests/SmithyFormURLTests/* - - Tests/SmithyTimestampsTests/* - - Tests/WeatherSDKTests/* + - Tests/* - smithy-swift-codegen-test/build/* analyzer_rules: diff --git a/Package.swift b/Package.swift index 7a2ae8c25..44e736ce8 100644 --- a/Package.swift +++ b/Package.swift @@ -29,6 +29,8 @@ let package = Package( ], products: [ .library(name: "ClientRuntime", targets: ["ClientRuntime"]), + .library(name: "SmithyRetriesAPI", targets: ["SmithyRetriesAPI"]), + .library(name: "SmithyRetries", targets: ["SmithyRetries"]), .library(name: "SmithyReadWrite", targets: ["SmithyReadWrite"]), .library(name: "SmithyXML", targets: ["SmithyXML"]), .library(name: "SmithyJSON", targets: ["SmithyJSON"]), @@ -43,6 +45,8 @@ let package = Package( .target( name: "ClientRuntime", dependencies: [ + "SmithyRetriesAPI", + "SmithyRetries", "SmithyXML", "SmithyJSON", "SmithyFormURL", @@ -53,6 +57,19 @@ let package = Package( .copy("PrivacyInfo.xcprivacy") ] ), + .target( + name: "SmithyRetriesAPI" + ), + .target( + name: "SmithyRetries", + dependencies: [ + .product(name: "AwsCommonRuntimeKit", package: "aws-crt-swift"), + ] + ), + .testTarget( + name: "SmithyRetriesTests", + dependencies: ["ClientRuntime", "SmithyRetriesAPI", "SmithyRetries"] + ), .target( name: "SmithyReadWrite", dependencies: [ @@ -121,11 +138,11 @@ func addTestServiceTargets() { package.targets += [ .target( name: "WeatherSDK", - dependencies: ["SmithyTestUtil", "ClientRuntime"] + dependencies: ["SmithyTestUtil", "ClientRuntime", "SmithyRetriesAPI", "SmithyRetries"] ), .testTarget( name: "WeatherSDKTests", - dependencies: ["WeatherSDK"] + dependencies: ["WeatherSDK", "SmithyTestUtil"] ) ] } diff --git a/Sources/ClientRuntime/Config/DefaultClientConfiguration.swift b/Sources/ClientRuntime/Config/DefaultClientConfiguration.swift index 17e24c31d..30df0a5b2 100644 --- a/Sources/ClientRuntime/Config/DefaultClientConfiguration.swift +++ b/Sources/ClientRuntime/Config/DefaultClientConfiguration.swift @@ -5,6 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 // +import struct SmithyRetriesAPI.RetryStrategyOptions + public protocol DefaultClientConfiguration: ClientConfiguration { /// The configuration for retry of failed network requests. /// diff --git a/Sources/ClientRuntime/Config/DefaultSDKRuntimeConfiguration.swift b/Sources/ClientRuntime/Config/DefaultSDKRuntimeConfiguration.swift index 67d32a548..2f3197c00 100644 --- a/Sources/ClientRuntime/Config/DefaultSDKRuntimeConfiguration.swift +++ b/Sources/ClientRuntime/Config/DefaultSDKRuntimeConfiguration.swift @@ -5,6 +5,12 @@ // SPDX-License-Identifier: Apache-2.0 // +import protocol SmithyRetriesAPI.RetryStrategy +import protocol SmithyRetriesAPI.RetryErrorInfoProvider +import struct SmithyRetriesAPI.RetryStrategyOptions +import struct SmithyRetries.DefaultRetryStrategy +import struct SmithyRetries.ExponentialBackoffStrategy + /// Provides configuration options for a Smithy-based service. public struct DefaultSDKRuntimeConfiguration { @@ -107,7 +113,9 @@ public extension DefaultSDKRuntimeConfiguration { /// The retry strategy options to use when none is provided. /// /// Defaults to options with the defaults defined in `RetryStrategyOptions`. - static var defaultRetryStrategyOptions: RetryStrategyOptions { RetryStrategyOptions() } + static var defaultRetryStrategyOptions: RetryStrategyOptions { + RetryStrategyOptions(backoffStrategy: ExponentialBackoffStrategy()) + } /// The log mode to use when none is provided /// diff --git a/Sources/ClientRuntime/Middleware/RetryMiddleware.swift b/Sources/ClientRuntime/Middleware/RetryMiddleware.swift index a44008cb1..0a4eb2291 100644 --- a/Sources/ClientRuntime/Middleware/RetryMiddleware.swift +++ b/Sources/ClientRuntime/Middleware/RetryMiddleware.swift @@ -10,6 +10,9 @@ import struct Foundation.Locale import struct Foundation.TimeInterval import struct Foundation.TimeZone import struct Foundation.UUID +import protocol SmithyRetriesAPI.RetryStrategy +import protocol SmithyRetriesAPI.RetryErrorInfoProvider +import struct SmithyRetriesAPI.RetryStrategyOptions public struct RetryMiddleware AwsCommonRuntimeKit.RetryError { + switch self { + case .transient: return .transient + case .throttling: return .throttling + case .serverError: return .serverError + case .clientError: return .clientError + } + } +} diff --git a/Sources/ClientRuntime/Retries/RetryBackoffStrategy.swift b/Sources/SmithyRetriesAPI/RetryBackoffStrategy.swift similarity index 100% rename from Sources/ClientRuntime/Retries/RetryBackoffStrategy.swift rename to Sources/SmithyRetriesAPI/RetryBackoffStrategy.swift diff --git a/Sources/SmithyRetriesAPI/RetryError.swift b/Sources/SmithyRetriesAPI/RetryError.swift new file mode 100644 index 000000000..b734f9511 --- /dev/null +++ b/Sources/SmithyRetriesAPI/RetryError.swift @@ -0,0 +1,14 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +/// Errors that may be thrown when an operation is retried unsuccessfully. +public enum RetryError: Error { + + /// The maximum number of allowed retry attempts were made, + /// but the operation could not be successfully completed. + case maxAttemptsReached +} diff --git a/Sources/ClientRuntime/Retries/RetryErrorInfo.swift b/Sources/SmithyRetriesAPI/RetryErrorInfo.swift similarity index 75% rename from Sources/ClientRuntime/Retries/RetryErrorInfo.swift rename to Sources/SmithyRetriesAPI/RetryErrorInfo.swift index 536d497d1..d2bd9ece9 100644 --- a/Sources/ClientRuntime/Retries/RetryErrorInfo.swift +++ b/Sources/SmithyRetriesAPI/RetryErrorInfo.swift @@ -7,6 +7,10 @@ import struct Foundation.TimeInterval +/// A set of information fields that are derived from an error thrown when connecting to a service. +/// +/// The `RetryErrorInfoProvider` creates an instance of this structure, which is then used by the +/// `RetryStrategy` to determine whether & how to retry a failed attempt. public struct RetryErrorInfo: Equatable { /// The general nature of the cause of this retryable error. diff --git a/Sources/ClientRuntime/Retries/RetryErrorInfoProvider.swift b/Sources/SmithyRetriesAPI/RetryErrorInfoProvider.swift similarity index 100% rename from Sources/ClientRuntime/Retries/RetryErrorInfoProvider.swift rename to Sources/SmithyRetriesAPI/RetryErrorInfoProvider.swift diff --git a/Sources/ClientRuntime/Retries/RetryErrorType.swift b/Sources/SmithyRetriesAPI/RetryErrorType.swift similarity index 72% rename from Sources/ClientRuntime/Retries/RetryErrorType.swift rename to Sources/SmithyRetriesAPI/RetryErrorType.swift index b66ccf3f6..98c9fd99d 100644 --- a/Sources/ClientRuntime/Retries/RetryErrorType.swift +++ b/Sources/SmithyRetriesAPI/RetryErrorType.swift @@ -4,8 +4,11 @@ // // SPDX-License-Identifier: Apache-2.0 // -import AwsCommonRuntimeKit +/// This enum is a general classification that is assigned to errors that resulted from a failed attempt to perform +/// an operation. +/// +/// The retry strategy uses this classification to decide whether and when to retry a failed attempt. public enum RetryErrorType: Equatable { /// This is a connection level error such as a socket timeout, socket connect error, tls negotiation timeout etc... @@ -22,15 +25,3 @@ public enum RetryErrorType: Equatable { /// Doesn’t count against any budgets. This could be something like a 401 challenge in HTTP. case clientError } - -public extension RetryErrorType { - - func toCRTType() -> AwsCommonRuntimeKit.RetryError { - switch self { - case .transient: return .transient - case .throttling: return .throttling - case .serverError: return .serverError - case .clientError: return .clientError - } - } -} diff --git a/Sources/ClientRuntime/Retries/RetryStrategy.swift b/Sources/SmithyRetriesAPI/RetryStrategy.swift similarity index 100% rename from Sources/ClientRuntime/Retries/RetryStrategy.swift rename to Sources/SmithyRetriesAPI/RetryStrategy.swift diff --git a/Sources/ClientRuntime/Retries/RetryStrategyOptions.swift b/Sources/SmithyRetriesAPI/RetryStrategyOptions.swift similarity index 95% rename from Sources/ClientRuntime/Retries/RetryStrategyOptions.swift rename to Sources/SmithyRetriesAPI/RetryStrategyOptions.swift index 078c1136d..c0e7aea28 100644 --- a/Sources/ClientRuntime/Retries/RetryStrategyOptions.swift +++ b/Sources/SmithyRetriesAPI/RetryStrategyOptions.swift @@ -36,12 +36,12 @@ public struct RetryStrategyOptions { /// Sets the initial available capacity for this retry strategy's quotas. /// /// Used only during testing, production uses the default values. - let availableCapacity: Int + public let availableCapacity: Int /// Sets the maximum capacity for this retry strategy's quotas. /// /// Used only during testing, production uses the default values. - let maxCapacity: Int + public let maxCapacity: Int /// Creates a new set of retry strategy options /// - Parameters: @@ -50,7 +50,7 @@ public struct RetryStrategyOptions { /// - availableCapacity: The number of available tokens in a retry quota. Defaults to 500. /// - maxCapacity: The max number of tokens in a retry quota. Defaults to 500. public init( - backoffStrategy: RetryBackoffStrategy = ExponentialBackoffStrategy(), + backoffStrategy: RetryBackoffStrategy, maxRetriesBase: Int = 2, availableCapacity: Int = 500, maxCapacity: Int = 500, diff --git a/Sources/ClientRuntime/Retries/RetryToken.swift b/Sources/SmithyRetriesAPI/RetryToken.swift similarity index 100% rename from Sources/ClientRuntime/Retries/RetryToken.swift rename to Sources/SmithyRetriesAPI/RetryToken.swift diff --git a/Sources/SmithyTestUtil/RequestTestUtil/MockRetryStrategy.swift b/Sources/SmithyTestUtil/RequestTestUtil/MockRetryStrategy.swift index 6f8dda7ec..710d411fc 100644 --- a/Sources/SmithyTestUtil/RequestTestUtil/MockRetryStrategy.swift +++ b/Sources/SmithyTestUtil/RequestTestUtil/MockRetryStrategy.swift @@ -5,6 +5,12 @@ // SPDX-License-Identifier: Apache-2.0 // +import protocol SmithyRetriesAPI.RetryStrategy +import protocol SmithyRetriesAPI.RetryToken +import struct SmithyRetriesAPI.RetryStrategyOptions +import struct SmithyRetriesAPI.RetryErrorInfo +import enum SmithyRetriesAPI.RetryError + @testable import ClientRuntime public struct MockRetryStrategy: RetryStrategy { diff --git a/Sources/WeatherSDK/WeatherClient.swift b/Sources/WeatherSDK/WeatherClient.swift index 9e4bba00b..595a9e1c3 100644 --- a/Sources/WeatherSDK/WeatherClient.swift +++ b/Sources/WeatherSDK/WeatherClient.swift @@ -5,6 +5,8 @@ import Foundation import Logging import SmithyJSON import SmithyReadWrite +import SmithyRetries +import SmithyRetriesAPI public class WeatherClient: Client { public static let clientName = "WeatherClient" @@ -28,7 +30,7 @@ extension WeatherClient { public class WeatherClientConfiguration: DefaultClientConfiguration & DefaultHttpClientConfiguration { public var telemetryProvider: ClientRuntime.TelemetryProvider - public var retryStrategyOptions: ClientRuntime.RetryStrategyOptions + public var retryStrategyOptions: SmithyRetriesAPI.RetryStrategyOptions public var clientLogMode: ClientRuntime.ClientLogMode @@ -46,7 +48,7 @@ extension WeatherClient { internal let logger: ClientRuntime.LogAgent - private init(_ telemetryProvider: ClientRuntime.TelemetryProvider, _ retryStrategyOptions: ClientRuntime.RetryStrategyOptions, _ clientLogMode: ClientRuntime.ClientLogMode, _ endpoint: Swift.String?, _ idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator, _ httpClientEngine: ClientRuntime.HTTPClient, _ httpClientConfiguration: ClientRuntime.HttpClientConfiguration, _ authSchemes: [ClientRuntime.AuthScheme]?, _ authSchemeResolver: ClientRuntime.AuthSchemeResolver) { + private init(_ telemetryProvider: ClientRuntime.TelemetryProvider, _ retryStrategyOptions: SmithyRetriesAPI.RetryStrategyOptions, _ clientLogMode: ClientRuntime.ClientLogMode, _ endpoint: Swift.String?, _ idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator, _ httpClientEngine: ClientRuntime.HTTPClient, _ httpClientConfiguration: ClientRuntime.HttpClientConfiguration, _ authSchemes: [ClientRuntime.AuthScheme]?, _ authSchemeResolver: ClientRuntime.AuthSchemeResolver) { self.telemetryProvider = telemetryProvider self.retryStrategyOptions = retryStrategyOptions self.clientLogMode = clientLogMode @@ -59,7 +61,7 @@ extension WeatherClient { self.logger = telemetryProvider.loggerProvider.getLogger(name: WeatherClient.clientName) } - public convenience init(telemetryProvider: ClientRuntime.TelemetryProvider? = nil, retryStrategyOptions: ClientRuntime.RetryStrategyOptions? = nil, clientLogMode: ClientRuntime.ClientLogMode? = nil, endpoint: Swift.String? = nil, idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator? = nil, httpClientEngine: ClientRuntime.HTTPClient? = nil, httpClientConfiguration: ClientRuntime.HttpClientConfiguration? = nil, authSchemes: [ClientRuntime.AuthScheme]? = nil, authSchemeResolver: ClientRuntime.AuthSchemeResolver? = nil) throws { + public convenience init(telemetryProvider: ClientRuntime.TelemetryProvider? = nil, retryStrategyOptions: SmithyRetriesAPI.RetryStrategyOptions? = nil, clientLogMode: ClientRuntime.ClientLogMode? = nil, endpoint: Swift.String? = nil, idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator? = nil, httpClientEngine: ClientRuntime.HTTPClient? = nil, httpClientConfiguration: ClientRuntime.HttpClientConfiguration? = nil, authSchemes: [ClientRuntime.AuthScheme]? = nil, authSchemeResolver: ClientRuntime.AuthSchemeResolver? = nil) throws { self.init(telemetryProvider ?? ClientRuntime.DefaultTelemetry.provider, retryStrategyOptions ?? ClientConfigurationDefaults.defaultRetryStrategyOptions, clientLogMode ?? ClientConfigurationDefaults.defaultClientLogMode, endpoint, idempotencyTokenGenerator ?? ClientConfigurationDefaults.defaultIdempotencyTokenGenerator, httpClientEngine ?? ClientConfigurationDefaults.makeClient(httpClientConfiguration: httpClientConfiguration ?? ClientConfigurationDefaults.defaultHttpClientConfiguration), httpClientConfiguration ?? ClientConfigurationDefaults.defaultHttpClientConfiguration, authSchemes, authSchemeResolver ?? ClientConfigurationDefaults.defaultAuthSchemeResolver) } @@ -119,7 +121,7 @@ extension WeatherClient { operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: CreateCityInput.write(value:to:))) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(CreateCityOutput.httpOutput(from:), CreateCityOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -156,7 +158,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.ContentMD5Middleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetCityOutput.httpOutput(from:), GetCityOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -192,7 +194,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(GetCityAnnouncementsInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetCityAnnouncementsOutput.httpOutput(from:), GetCityAnnouncementsOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -228,7 +230,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(GetCityImageInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetCityImageOutput.httpOutput(from:), GetCityImageOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -259,7 +261,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(GetCurrentTimeInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetCurrentTimeOutput.httpOutput(from:), GetCurrentTimeOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -290,7 +292,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(GetForecastInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(GetForecastOutput.httpOutput(from:), GetForecastOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -324,7 +326,7 @@ extension WeatherClient { operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/octet-stream")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BlobBodyMiddleware(keyPath: \.payload)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(InvokeOutput.httpOutput(from:), InvokeOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -361,7 +363,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.QueryItemMiddleware(ListCitiesInput.queryItemProvider(_:))) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(ListCitiesOutput.httpOutput(from:), ListCitiesOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -392,7 +394,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlyFakeAuthInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyFakeAuthOutput.httpOutput(from:), OnlyFakeAuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -423,7 +425,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlyFakeAuthOptionalInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyFakeAuthOptionalOutput.httpOutput(from:), OnlyFakeAuthOptionalOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -454,7 +456,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlyHttpApiKeyAndBearerAuthInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpApiKeyAndBearerAuthOutput.httpOutput(from:), OnlyHttpApiKeyAndBearerAuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -485,7 +487,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlyHttpApiKeyAndBearerAuthReversedInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpApiKeyAndBearerAuthReversedOutput.httpOutput(from:), OnlyHttpApiKeyAndBearerAuthReversedOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -516,7 +518,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlyHttpApiKeyAuthInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpApiKeyAuthOutput.httpOutput(from:), OnlyHttpApiKeyAuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -547,7 +549,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlyHttpApiKeyAuthOptionalInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpApiKeyAuthOptionalOutput.httpOutput(from:), OnlyHttpApiKeyAuthOptionalOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -578,7 +580,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlyHttpBearerAuthInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpBearerAuthOutput.httpOutput(from:), OnlyHttpBearerAuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -609,7 +611,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlyHttpBearerAuthOptionalInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlyHttpBearerAuthOptionalOutput.httpOutput(from:), OnlyHttpBearerAuthOptionalOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -640,7 +642,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlySigv4AuthInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlySigv4AuthOutput.httpOutput(from:), OnlySigv4AuthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -671,7 +673,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(OnlySigv4AuthOptionalInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(OnlySigv4AuthOptionalOutput.httpOutput(from:), OnlySigv4AuthOptionalOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -702,7 +704,7 @@ extension WeatherClient { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware(SameAsServiceInput.urlPathProvider(_:))) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) operation.buildStep.intercept(position: .before, middleware: ClientRuntime.AuthSchemeMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(SameAsServiceOutput.httpOutput(from:), SameAsServiceOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) diff --git a/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift b/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift index a5d18d4af..904791b27 100644 --- a/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift +++ b/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift @@ -8,6 +8,8 @@ import XCTest @testable import ClientRuntime +import SmithyRetriesAPI +import SmithyRetries import SmithyJSON class OrchestratorTests: XCTestCase { @@ -183,7 +185,7 @@ class OrchestratorTests: XCTestCase { } struct ThrowingRetryStrategy: RetryStrategy { - init(options: ClientRuntime.RetryStrategyOptions) {} + init(options: RetryStrategyOptions) {} public func acquireInitialRetryToken(tokenScope: String) async throws -> DefaultRetryToken { throw TestError(value: "1") @@ -226,7 +228,7 @@ class OrchestratorTests: XCTestCase { return .failure(try UnknownHTTPServiceError.makeError(baseError: baseError)) } }) - .retryStrategy(DefaultRetryStrategy(options: RetryStrategyOptions())) + .retryStrategy(DefaultRetryStrategy(options: RetryStrategyOptions(backoffStrategy: ExponentialBackoffStrategy()))) .retryErrorInfoProvider({ e in trace.append("errorInfo") return DefaultRetryErrorInfoProvider.errorInfo(for: e) @@ -520,7 +522,7 @@ class OrchestratorTests: XCTestCase { let initialTokenTrace = Trace() let initialToken = await asyncResult { return try await self.traceOrchestrator(trace: initialTokenTrace) - .retryStrategy(ThrowingRetryStrategy(options: RetryStrategyOptions())) + .retryStrategy(ThrowingRetryStrategy(options: RetryStrategyOptions(backoffStrategy: ExponentialBackoffStrategy()))) .build() .execute(input: TestInput(foo: "")) } diff --git a/Tests/ClientRuntimeTests/Retry/DefaultRetryErrorInfoProviderTests.swift b/Tests/SmithyRetriesTests/DefaultRetryErrorInfoProviderTests.swift similarity index 100% rename from Tests/ClientRuntimeTests/Retry/DefaultRetryErrorInfoProviderTests.swift rename to Tests/SmithyRetriesTests/DefaultRetryErrorInfoProviderTests.swift diff --git a/Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/ClientSideRateLimiterTests.swift b/Tests/SmithyRetriesTests/DefaultRetryStrategy/ClientSideRateLimiterTests.swift similarity index 99% rename from Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/ClientSideRateLimiterTests.swift rename to Tests/SmithyRetriesTests/DefaultRetryStrategy/ClientSideRateLimiterTests.swift index 909c299dc..5b8e6b5c4 100644 --- a/Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/ClientSideRateLimiterTests.swift +++ b/Tests/SmithyRetriesTests/DefaultRetryStrategy/ClientSideRateLimiterTests.swift @@ -7,6 +7,7 @@ import Foundation import XCTest +@testable import SmithyRetries @testable import ClientRuntime final class ClientSideRateLimiterTests: XCTestCase { diff --git a/Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/DefaultRetryStrategyTests.swift b/Tests/SmithyRetriesTests/DefaultRetryStrategy/DefaultRetryStrategyTests.swift similarity index 98% rename from Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/DefaultRetryStrategyTests.swift rename to Tests/SmithyRetriesTests/DefaultRetryStrategy/DefaultRetryStrategyTests.swift index b0e8d84ad..fa930ddb9 100644 --- a/Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/DefaultRetryStrategyTests.swift +++ b/Tests/SmithyRetriesTests/DefaultRetryStrategy/DefaultRetryStrategyTests.swift @@ -7,6 +7,8 @@ import Foundation import XCTest +import SmithyRetriesAPI +@testable import SmithyRetries @testable import ClientRuntime final class DefaultRetryStrategyTests: XCTestCase { @@ -116,7 +118,7 @@ final class DefaultRetryStrategyTests: XCTestCase { do { try await subject.refreshRetryTokenForRetry(tokenToRenew: token1, errorInfo: retryableInfo) XCTFail("Should have failed") - } catch RetryError.insufficientQuota { + } catch DefaultRetryStrategy.Error.insufficientQuota { // success } catch { XCTFail("Unexpected error thrown") diff --git a/Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/ExponentialBackoffStrategyTests.swift b/Tests/SmithyRetriesTests/DefaultRetryStrategy/ExponentialBackoffStrategyTests.swift similarity index 98% rename from Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/ExponentialBackoffStrategyTests.swift rename to Tests/SmithyRetriesTests/DefaultRetryStrategy/ExponentialBackoffStrategyTests.swift index 4594ea0c9..8a05f052f 100644 --- a/Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/ExponentialBackoffStrategyTests.swift +++ b/Tests/SmithyRetriesTests/DefaultRetryStrategy/ExponentialBackoffStrategyTests.swift @@ -7,6 +7,7 @@ import Foundation import XCTest +@testable import SmithyRetries @testable import ClientRuntime final class ExponentialBackoffStrategyTests: XCTestCase { diff --git a/Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/RetryQuotaTests.swift b/Tests/SmithyRetriesTests/DefaultRetryStrategy/RetryQuotaTests.swift similarity index 99% rename from Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/RetryQuotaTests.swift rename to Tests/SmithyRetriesTests/DefaultRetryStrategy/RetryQuotaTests.swift index 75f9e9427..3327a510b 100644 --- a/Tests/ClientRuntimeTests/Retry/DefaultRetryStrategy/RetryQuotaTests.swift +++ b/Tests/SmithyRetriesTests/DefaultRetryStrategy/RetryQuotaTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest @testable import ClientRuntime +@testable import SmithyRetries final class RetryQuotaTests: XCTestCase { diff --git a/Tests/ClientRuntimeTests/Retry/RetryIntegrationTests.swift b/Tests/SmithyRetriesTests/RetryIntegrationTests.swift similarity index 99% rename from Tests/ClientRuntimeTests/Retry/RetryIntegrationTests.swift rename to Tests/SmithyRetriesTests/RetryIntegrationTests.swift index 6a30ab1ba..50925d7cb 100644 --- a/Tests/ClientRuntimeTests/Retry/RetryIntegrationTests.swift +++ b/Tests/SmithyRetriesTests/RetryIntegrationTests.swift @@ -8,6 +8,8 @@ import SmithyReadWrite import SmithyXML import XCTest +import SmithyRetriesAPI +@testable import SmithyRetries @testable import ClientRuntime // This test class reproduces the "Standard Mode" test cases defined in "Retry Behavior 2.0" diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt index 52fcfea01..c8d955db6 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt @@ -91,8 +91,6 @@ object ClientRuntimeTypes { val SDKLogLevel = runtimeSymbol("SDKLogLevel") val ClientLogMode = runtimeSymbol("ClientLogMode") val IdempotencyTokenGenerator = runtimeSymbol("IdempotencyTokenGenerator") - val DefaultRetryStrategy = runtimeSymbol("DefaultRetryStrategy") - val RetryStrategyOptions = runtimeSymbol("RetryStrategyOptions") val DefaultRetryErrorInfoProvider = runtimeSymbol("DefaultRetryErrorInfoProvider") val PaginateToken = runtimeSymbol("PaginateToken") val PaginatorSequence = runtimeSymbol("PaginatorSequence") diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt index 8edfb2a85..64532fdd9 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt @@ -26,6 +26,22 @@ enum class SwiftDependency( Resources.computeAbsolutePath("smithy-swift", "", "SMITHY_SWIFT_CI_DIR"), "smithy-swift" ), + SMITHY_RETRIES_API( + "SmithyRetriesAPI", + "main", + "0.1.0", + "https://github.com/smithy-lang/smithy-swift", + Resources.computeAbsolutePath("smithy-swift", "", "SMITHY_SWIFT_CI_DIR"), + "smithy-swift" + ), + SMITHY_RETRIES( + "SmithyRetries", + "main", + "0.1.0", + "https://github.com/smithy-lang/smithy-swift", + Resources.computeAbsolutePath("smithy-swift", "", "SMITHY_SWIFT_CI_DIR"), + "smithy-swift" + ), XCTest("XCTest", null, "", "", "", ""), SMITHY_TEST_UTIL( "SmithyTestUtil", diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/config/DefaultClientConfiguration.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/config/DefaultClientConfiguration.kt index c9a2efd48..eaeb6a2c2 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/config/DefaultClientConfiguration.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/config/DefaultClientConfiguration.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.config.ClientConfiguration.Companion.runtimeSymbol import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.model.toOptional +import software.amazon.smithy.swift.codegen.swiftmodules.SmithyRetriesAPITypes class DefaultClientConfiguration : ClientConfiguration { override val swiftProtocolName: Symbol @@ -25,7 +26,7 @@ class DefaultClientConfiguration : ClientConfiguration { ), ConfigProperty( "retryStrategyOptions", - ClientRuntimeTypes.Core.RetryStrategyOptions, + SmithyRetriesAPITypes.RetryStrategyOptions, "ClientConfigurationDefaults.defaultRetryStrategyOptions" ), ConfigProperty( diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt index 3fb43d4a1..4a7048a31 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt @@ -105,6 +105,7 @@ open class HttpProtocolServiceClient( val properties: List = ctx.integrations .flatMap { it.clientConfigurations(ctx).flatMap { it.getProperties(ctx) } } .let { overrideConfigProperties(it) } + properties.forEach { writer.addImport(it.type) } renderConfigClassVariables(serviceSymbol, properties) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/RetryMiddleware.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/RetryMiddleware.kt index ae3d55815..66efaba40 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/RetryMiddleware.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/middlewares/RetryMiddleware.kt @@ -10,12 +10,14 @@ import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.swift.codegen.ClientRuntimeTypes +import software.amazon.smithy.swift.codegen.SwiftDependency import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.middlewares.handlers.MiddlewareShapeUtils import software.amazon.smithy.swift.codegen.middleware.MiddlewarePosition import software.amazon.smithy.swift.codegen.middleware.MiddlewareRenderable import software.amazon.smithy.swift.codegen.middleware.MiddlewareStep +import software.amazon.smithy.swift.codegen.swiftmodules.SmithyRetriesTypes class RetryMiddleware( val model: Model, @@ -31,15 +33,19 @@ class RetryMiddleware( override fun render(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, op: OperationShape, operationStackName: String) { if (ctx.settings.useInterceptors) { - writer.write("builder.retryStrategy(\$N(options: config.retryStrategyOptions))", ClientRuntimeTypes.Core.DefaultRetryStrategy) + writer.addImport(SwiftDependency.SMITHY_RETRIES.target) + writer.write("builder.retryStrategy(\$N(options: config.retryStrategyOptions))", SmithyRetriesTypes.DefaultRetryStrategy) writer.write("builder.retryErrorInfoProvider(\$N.errorInfo(for:))", retryErrorInfoProviderSymbol) } else { val output = MiddlewareShapeUtils.outputSymbol(symbolProvider, model, op) - val outputError = MiddlewareShapeUtils.outputErrorSymbol(op) + writer.addImport(SwiftDependency.SMITHY_RETRIES.target) writer.write( - "$operationStackName.${middlewareStep.stringValue()}.intercept(position: ${position.stringValue()}, middleware: \$N<\$N, \$N, \$N>(options: config.retryStrategyOptions))", + "\$L.\$L.intercept(position: \$L, middleware: \$N<\$N, \$N, \$N>(options: config.retryStrategyOptions))", + operationStackName, + middlewareStep.stringValue(), + position.stringValue(), ClientRuntimeTypes.Middleware.RetryMiddleware, - ClientRuntimeTypes.Core.DefaultRetryStrategy, + SmithyRetriesTypes.DefaultRetryStrategy, retryErrorInfoProviderSymbol, output ) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRetriesAPITypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRetriesAPITypes.kt new file mode 100644 index 000000000..94dadbfd4 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRetriesAPITypes.kt @@ -0,0 +1,21 @@ +package software.amazon.smithy.swift.codegen.swiftmodules + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.swift.codegen.SwiftDependency +import software.amazon.smithy.swift.codegen.model.buildSymbol + +/** + * Commonly used runtime types. Provides a single definition of a runtime symbol such that codegen isn't littered + * with inline symbol creation which makes refactoring of the runtime more difficult and error prone. + * + * NOTE: Not all symbols need be added here but it doesn't hurt to define runtime symbols once. + */ +object SmithyRetriesAPITypes { + val RetryStrategyOptions = runtimeSymbol("RetryStrategyOptions") +} + +private fun runtimeSymbol(name: String): Symbol = buildSymbol { + this.name = name + this.namespace = SwiftDependency.SMITHY_RETRIES_API.target + this.dependency(SwiftDependency.SMITHY_RETRIES_API) +} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRetriesTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRetriesTypes.kt new file mode 100644 index 000000000..82a5dc409 --- /dev/null +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/SmithyRetriesTypes.kt @@ -0,0 +1,21 @@ +package software.amazon.smithy.swift.codegen.swiftmodules + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.swift.codegen.SwiftDependency +import software.amazon.smithy.swift.codegen.model.buildSymbol + +/** + * Commonly used runtime types. Provides a single definition of a runtime symbol such that codegen isn't littered + * with inline symbol creation which makes refactoring of the runtime more difficult and error prone. + * + * NOTE: Not all symbols need be added here but it doesn't hurt to define runtime symbols once. + */ +object SmithyRetriesTypes { + val DefaultRetryStrategy = runtimeSymbol("DefaultRetryStrategy") +} + +private fun runtimeSymbol(name: String): Symbol = buildSymbol { + this.name = name + this.namespace = SwiftDependency.SMITHY_RETRIES.target + this.dependency(SwiftDependency.SMITHY_RETRIES) +} diff --git a/smithy-swift-codegen/src/test/kotlin/EventStreamTests.kt b/smithy-swift-codegen/src/test/kotlin/EventStreamTests.kt index 0a0683f34..7f31c9373 100644 --- a/smithy-swift-codegen/src/test/kotlin/EventStreamTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/EventStreamTests.kt @@ -220,7 +220,7 @@ extension EventStreamTestClientTypes.TestStream { operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.EventStreamBodyMiddleware(keyPath: \.value, defaultBody: "{}", marshalClosure: EventStreamTestClientTypes.TestStream.marshal)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(TestStreamOpOutput.httpOutput(from:), TestStreamOpOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) diff --git a/smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt index 73f7dd1ae..348d21984 100644 --- a/smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt @@ -37,7 +37,7 @@ extension RestJsonProtocolClient { public class RestJsonProtocolClientConfiguration: DefaultClientConfiguration & DefaultHttpClientConfiguration { public var telemetryProvider: ClientRuntime.TelemetryProvider - public var retryStrategyOptions: ClientRuntime.RetryStrategyOptions + public var retryStrategyOptions: SmithyRetriesAPI.RetryStrategyOptions public var clientLogMode: ClientRuntime.ClientLogMode @@ -53,7 +53,7 @@ extension RestJsonProtocolClient { public var authSchemeResolver: ClientRuntime.AuthSchemeResolver - private init(_ telemetryProvider: ClientRuntime.TelemetryProvider, _ retryStrategyOptions: ClientRuntime.RetryStrategyOptions, _ clientLogMode: ClientRuntime.ClientLogMode, _ endpoint: Swift.String?, _ idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator, _ httpClientEngine: ClientRuntime.HTTPClient, _ httpClientConfiguration: ClientRuntime.HttpClientConfiguration, _ authSchemes: [ClientRuntime.AuthScheme]?, _ authSchemeResolver: ClientRuntime.AuthSchemeResolver) { + private init(_ telemetryProvider: ClientRuntime.TelemetryProvider, _ retryStrategyOptions: SmithyRetriesAPI.RetryStrategyOptions, _ clientLogMode: ClientRuntime.ClientLogMode, _ endpoint: Swift.String?, _ idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator, _ httpClientEngine: ClientRuntime.HTTPClient, _ httpClientConfiguration: ClientRuntime.HttpClientConfiguration, _ authSchemes: [ClientRuntime.AuthScheme]?, _ authSchemeResolver: ClientRuntime.AuthSchemeResolver) { self.telemetryProvider = telemetryProvider self.retryStrategyOptions = retryStrategyOptions self.clientLogMode = clientLogMode @@ -65,7 +65,7 @@ extension RestJsonProtocolClient { self.authSchemeResolver = authSchemeResolver } - public convenience init(telemetryProvider: ClientRuntime.TelemetryProvider? = nil, retryStrategyOptions: ClientRuntime.RetryStrategyOptions? = nil, clientLogMode: ClientRuntime.ClientLogMode? = nil, endpoint: Swift.String? = nil, idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator? = nil, httpClientEngine: ClientRuntime.HTTPClient? = nil, httpClientConfiguration: ClientRuntime.HttpClientConfiguration? = nil, authSchemes: [ClientRuntime.AuthScheme]? = nil, authSchemeResolver: ClientRuntime.AuthSchemeResolver? = nil) throws { + public convenience init(telemetryProvider: ClientRuntime.TelemetryProvider? = nil, retryStrategyOptions: SmithyRetriesAPI.RetryStrategyOptions? = nil, clientLogMode: ClientRuntime.ClientLogMode? = nil, endpoint: Swift.String? = nil, idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator? = nil, httpClientEngine: ClientRuntime.HTTPClient? = nil, httpClientConfiguration: ClientRuntime.HttpClientConfiguration? = nil, authSchemes: [ClientRuntime.AuthScheme]? = nil, authSchemeResolver: ClientRuntime.AuthSchemeResolver? = nil) throws { self.init(telemetryProvider ?? ClientRuntime.DefaultTelemetry.provider, retryStrategyOptions ?? ClientConfigurationDefaults.defaultRetryStrategyOptions, clientLogMode ?? ClientConfigurationDefaults.defaultClientLogMode, endpoint, idempotencyTokenGenerator ?? ClientConfigurationDefaults.defaultIdempotencyTokenGenerator, httpClientEngine ?? ClientConfigurationDefaults.makeClient(httpClientConfiguration: httpClientConfiguration ?? ClientConfigurationDefaults.defaultHttpClientConfiguration), httpClientConfiguration ?? ClientConfigurationDefaults.defaultHttpClientConfiguration, authSchemes, authSchemeResolver ?? ClientConfigurationDefaults.defaultAuthSchemeResolver) } @@ -100,6 +100,7 @@ public struct RestJsonProtocolClientLogHandlerFactory: ClientRuntime.SDKLogHandl """ contents.shouldContainOnlyOnce(expected) } + @Test fun `it renders host prefix with label in context correctly`() { val context = setupTests("host-prefix-operation.smithy", "com.test#Example") @@ -155,7 +156,7 @@ public struct RestJsonProtocolClientLogHandlerFactory: ClientRuntime.SDKLogHandl operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: AllocateWidgetInput.write(value:to:))) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(AllocateWidgetOutput.httpOutput(from:), AllocateWidgetOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -192,7 +193,7 @@ public struct RestJsonProtocolClientLogHandlerFactory: ClientRuntime.SDKLogHandl operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: UnsignedFooBlobStreamInput.write(value:to:))) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware(requiresLength: false, unsignedPayload: true)) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(UnsignedFooBlobStreamOutput.httpOutput(from:), UnsignedFooBlobStreamOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -229,7 +230,7 @@ public struct RestJsonProtocolClientLogHandlerFactory: ClientRuntime.SDKLogHandl operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/octet-stream")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BlobStreamBodyMiddleware(keyPath: \.payload1)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware(requiresLength: true, unsignedPayload: false)) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(ExplicitBlobStreamWithLengthOutput.httpOutput(from:), ExplicitBlobStreamWithLengthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) @@ -266,7 +267,7 @@ public struct RestJsonProtocolClientLogHandlerFactory: ClientRuntime.SDKLogHandl operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/octet-stream")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.BlobStreamBodyMiddleware(keyPath: \.payload1)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware(requiresLength: true, unsignedPayload: true)) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.SignerMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware(UnsignedFooBlobStreamWithLengthOutput.httpOutput(from:), UnsignedFooBlobStreamWithLengthOutputError.httpError(from:))) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) diff --git a/smithy-swift-codegen/src/test/kotlin/RetryMiddlewareTests.kt b/smithy-swift-codegen/src/test/kotlin/RetryMiddlewareTests.kt index e405571b0..b6c7b8acf 100644 --- a/smithy-swift-codegen/src/test/kotlin/RetryMiddlewareTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/RetryMiddlewareTests.kt @@ -8,7 +8,7 @@ class RetryMiddlewareTests { val context = setupTests("Isolated/contentmd5checksum.smithy", "aws.protocoltests.restxml#RestXml") val contents = getFileContents(context.manifest, "/RestXml/RestXmlProtocolClient.swift") val expectedContents = """ - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) """.trimIndent() contents.shouldContainOnlyOnce(expectedContents) }