diff --git a/.swiftlint.yml b/.swiftlint.yml index dd6921273ae..0a9a1ac3f5c 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -16,6 +16,7 @@ disabled_rules: - syntactic_sugar - unused_capture_list - nesting + - operator_whitespace - large_tuple opt_in_rules: @@ -32,6 +33,8 @@ empty_enum_arguments: error function_body_length: warning: 100 error: 150 +generic_type_name: + max_length: 48 identifier_name: excluded: - id diff --git a/Sources/Core/AWSClientRuntime/AWSClientConfiguration.swift b/Sources/Core/AWSClientRuntime/AWSClientConfiguration.swift index b9f5db3ef88..37b39d7ae50 100644 --- a/Sources/Core/AWSClientRuntime/AWSClientConfiguration.swift +++ b/Sources/Core/AWSClientRuntime/AWSClientConfiguration.swift @@ -1,18 +1,212 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// import ClientRuntime -public protocol AWSRuntimeConfiguration { - var credentialsProvider: CredentialsProviding { get set } - var region: String? { get set } - var signingRegion: String? {get set} - var regionResolver: RegionResolver? {get set} - var frameworkMetadata: FrameworkMetadata? {get set} - var useFIPS: Bool? {get set} - var useDualStack: Bool? {get set} +/// Provides configuration properties for AWS services. +/// +/// This type is used to configure every AWS service client. It is specialized with a `AWSServiceSpecificConfiguration` that contains +/// service-specific configuration properties. +/// +/// The service-specific configuration is code-generated for each AWS service; see the generated client code for implementation. +/// +/// A custom `typealias` of this type is code-generated for each AWS service; i.e. +/// ``` +/// public typealias S3Client.S3ClientConfiguration = AWSClientConfiguration +/// ``` +public class AWSClientConfiguration { + + /// The custom encoder to be used for encoding models for transmission. + /// + /// If no encoder is provided, one will be provided by the SDK. + public var encoder: RequestEncoder? + + /// The custom decoder to be used for decoding models from a response. + /// + /// If no decoder is provided, one will be provided by the SDK. + public var decoder: ResponseDecoder? + + /// The HTTP client engine to be used for HTTP requests. + /// + /// If none is provided, AWS provides its own HTTP engine for use. + public var httpClientEngine: HttpClientEngine + + /// Configuration for the HTTP client. + public var httpClientConfiguration: HttpClientConfiguration + + /// A token generator to ensure idempotency of requests. + public var idempotencyTokenGenerator: IdempotencyTokenGenerator + + /// A logger to be used by the SDK for logging events. + public var logger: LogAgent + + /// The log level to be used when logging. + public var clientLogMode: ClientLogMode + + /// The configuation for retry of failed network requests. + /// + /// Default options are provided if none are set. + public var retryStrategyOptions: RetryStrategyOptions + + /// The network host to use. + /// + /// If none is provided, the SDK selects the most appropriate host for the AWS service in use + /// + /// Note: non-secure HTTP is not supported at this time. + public var endpoint: String? + + /// The credentials provider to be used for AWS credentials. + /// + /// If no credentials provider is supplied, the SDK will look for credentials in the environment, then in the `~/.aws/credentials` file. + public var credentialsProvider: CredentialsProviding + + /// The AWS region to use, i.e. `us-east-1` or `us-west-2`, etc. + /// + /// If no region is specified here, one must be specified in the `~/.aws/configuration` file. + public var region: String? + + /// The signing region to be used for signing AWS requests. + /// + /// If none is specified, it is supplied by the SDK. + public var signingRegion: String? + + /// Framework Metadata for the client. + /// + /// If none is supplied, the SDK will supply default metadata. + public var frameworkMetadata: FrameworkMetadata? + + /// Specifies whether FIPS endpoints should be used. + public var useFIPS: Bool? + + /// Specifies whether dual-stack endpoints should be used. + public var useDualStack: Bool? + + /// A structure containing AWS service-specific properties. + /// + /// This structure is custom code-generated for each AWS service. + public var serviceSpecific: ServiceSpecificConfiguration + + /// Internal designated init + /// All convenience inits should call this. + init( + // these params have no labels to distinguish this init from the similar convenience inits below + _ credentialsProvider: AWSClientRuntime.CredentialsProviding, + _ endpoint: Swift.String?, + _ serviceSpecific: ServiceSpecificConfiguration?, + _ frameworkMetadata: AWSClientRuntime.FrameworkMetadata?, + _ region: Swift.String?, + _ signingRegion: Swift.String?, + _ useDualStack: Swift.Bool?, + _ useFIPS: Swift.Bool?, + _ retryStrategyOptions: RetryStrategyOptions? + ) throws { + typealias RuntimeConfigType = + DefaultSDKRuntimeConfiguration + + self.credentialsProvider = credentialsProvider + self.serviceSpecific = try serviceSpecific ?? ServiceSpecificConfiguration(endpointResolver: nil) + self.frameworkMetadata = frameworkMetadata + self.region = region + self.signingRegion = signingRegion ?? region + self.useDualStack = useDualStack + self.useFIPS = useFIPS + self.clientLogMode = RuntimeConfigType.defaultClientLogMode + self.httpClientConfiguration = RuntimeConfigType.defaultHttpClientConfiguration + self.httpClientEngine = RuntimeConfigType.defaultHttpClientEngine + self.idempotencyTokenGenerator = RuntimeConfigType.defaultIdempotencyTokenGenerator + self.logger = RuntimeConfigType.defaultLogger(clientName: self.serviceSpecific.clientName) + self.retryStrategyOptions = retryStrategyOptions ?? RuntimeConfigType.defaultRetryStrategyOptions + } } -public typealias AWSClientConfiguration = SDKRuntimeConfiguration & AWSRuntimeConfiguration +// MARK: - Convenience initializers + +extension AWSClientConfiguration { + + public convenience init(region: String) throws { + try self.init(region: region, serviceSpecific: try ServiceSpecificConfiguration(endpointResolver: nil)) + } + + public convenience init() async throws { + try await self.init(serviceSpecific: try ServiceSpecificConfiguration(endpointResolver: nil)) + } + + /// Creates a configuration asynchronously + public convenience init( + credentialsProvider: AWSClientRuntime.CredentialsProviding? = nil, + endpoint: Swift.String? = nil, + serviceSpecific: ServiceSpecificConfiguration? = nil, + frameworkMetadata: AWSClientRuntime.FrameworkMetadata? = nil, + region: Swift.String? = nil, + regionResolver: AWSClientRuntime.RegionResolver? = nil, + signingRegion: Swift.String? = nil, + useDualStack: Swift.Bool? = nil, + useFIPS: Swift.Bool? = nil, + retryStrategyOptions: RetryStrategyOptions? = nil + ) async throws { + let fileBasedConfig = try await CRTFileBasedConfiguration.makeAsync() + let resolvedRegionResolver = try regionResolver ?? DefaultRegionResolver { _, _ in fileBasedConfig } + let resolvedRegion: String? + if let region = region { + resolvedRegion = region + } else { + resolvedRegion = await resolvedRegionResolver.resolveRegion() + } + let resolvedCredentialsProvider: AWSClientRuntime.CredentialsProviding + if let credentialsProvider = credentialsProvider { + resolvedCredentialsProvider = credentialsProvider + } else { + resolvedCredentialsProvider = try DefaultChainCredentialsProvider(fileBasedConfig: fileBasedConfig) + } + try self.init( + resolvedCredentialsProvider, + endpoint, + serviceSpecific, + frameworkMetadata, + resolvedRegion, + signingRegion, + useDualStack, + useFIPS, + retryStrategyOptions + ) + } + + public convenience init( + region: Swift.String, + credentialsProvider: AWSClientRuntime.CredentialsProviding? = nil, + endpoint: Swift.String? = nil, + serviceSpecific: ServiceSpecificConfiguration? = nil, + frameworkMetadata: AWSClientRuntime.FrameworkMetadata? = nil, + signingRegion: Swift.String? = nil, + useDualStack: Swift.Bool? = nil, + useFIPS: Swift.Bool? = nil, + retryStrategyOptions: RetryStrategyOptions? = nil + ) throws { + let resolvedCredentialsProvider: CredentialsProviding + if let credentialsProvider = credentialsProvider { + resolvedCredentialsProvider = credentialsProvider + } else { + let fileBasedConfig = try CRTFileBasedConfiguration.make() + resolvedCredentialsProvider = try DefaultChainCredentialsProvider(fileBasedConfig: fileBasedConfig) + } + try self.init( + resolvedCredentialsProvider, + endpoint, + serviceSpecific, + frameworkMetadata, + region, + signingRegion, + useDualStack, + useFIPS, + retryStrategyOptions + ) + } + + public var partitionID: String? { + return "\(serviceSpecific.clientName) - \(region ?? "")" + } +} diff --git a/Sources/Core/AWSClientRuntime/AWSServiceSpecificConfiguration.swift b/Sources/Core/AWSClientRuntime/AWSServiceSpecificConfiguration.swift new file mode 100644 index 00000000000..2cba8df730d --- /dev/null +++ b/Sources/Core/AWSClientRuntime/AWSServiceSpecificConfiguration.swift @@ -0,0 +1,35 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +/// Contains config properties specific to one AWS service +/// +/// A custom service-specific configuration that conforms to this protocol will be code-generated +/// for every AWS service, and an instance of it will be exposed by `AWSClientConfiguration`'s +/// `serviceSpecific` property. +public protocol AWSServiceSpecificConfiguration { + + /// The type for the service's endpoint resolver. + associatedtype AWSServiceEndpointResolver + + /// The name of the service, i.e. "STS". + /// + /// Derived from the service's `sdkId` property. + var serviceName: String { get } + + /// The name of the service's client, i.e. "STSClient". + var clientName: String { get } + + /// An endpoint resolver for this service. + /// + /// If none is provided at compile time, the service will provide one. + var endpointResolver: AWSServiceEndpointResolver { get } + + /// Creates a service-specific configuration for this service + /// - Parameter endpointResolver: An endpoint resolver for the service, or `nil` to let + /// the service resolve its own endpoint. + init(endpointResolver: AWSServiceEndpointResolver?) throws +} diff --git a/Sources/Core/AWSClientRuntime/Endpoints/AWSEndpointsRuleEngine.swift b/Sources/Core/AWSClientRuntime/Endpoints/AWSEndpointsRuleEngine.swift index 55839e9bbeb..a93d1a735e2 100644 --- a/Sources/Core/AWSClientRuntime/Endpoints/AWSEndpointsRuleEngine.swift +++ b/Sources/Core/AWSClientRuntime/Endpoints/AWSEndpointsRuleEngine.swift @@ -9,6 +9,7 @@ public class AWSEndpointsRuleEngine { let crtEngine: AwsCommonRuntimeKit.EndpointsRuleEngine public init(partitions: String, ruleSet: String) throws { + Utils.setupCRT() // ensures CRT is set up before calling the CRT endpoint rules engine crtEngine = try AwsCommonRuntimeKit.EndpointsRuleEngine(partitions: partitions, ruleSet: ruleSet) } diff --git a/Sources/Core/AWSClientRuntime/Errors/UnknownAWSHTTPServiceError.swift b/Sources/Core/AWSClientRuntime/Errors/UnknownAWSHTTPServiceError.swift index cd3af8ae768..653ffb05312 100644 --- a/Sources/Core/AWSClientRuntime/Errors/UnknownAWSHTTPServiceError.swift +++ b/Sources/Core/AWSClientRuntime/Errors/UnknownAWSHTTPServiceError.swift @@ -69,7 +69,12 @@ extension UnknownAWSHTTPServiceError { InvalidAccessKeyId.self ] if let Candidate = candidates.first(where: { $0.errorCode == typeName }) { - return Candidate.init(httpResponse: httpResponse, message: message, requestID: requestID, requestID2: requestID2) + return Candidate.init( + httpResponse: httpResponse, + message: message, + requestID: requestID, + requestID2: requestID2 + ) } return UnknownAWSHTTPServiceError( httpResponse: httpResponse, diff --git a/Sources/Core/AWSClientRuntime/FileBasedConfiguration/CRTFileBasedConfiguration.swift b/Sources/Core/AWSClientRuntime/FileBasedConfiguration/CRTFileBasedConfiguration.swift index 1a23aa98325..c5dec529ace 100644 --- a/Sources/Core/AWSClientRuntime/FileBasedConfiguration/CRTFileBasedConfiguration.swift +++ b/Sources/Core/AWSClientRuntime/FileBasedConfiguration/CRTFileBasedConfiguration.swift @@ -7,10 +7,14 @@ import AwsCommonRuntimeKit -@_spi(FileBasedConfig) public typealias CRTFileBasedConfiguration = AwsCommonRuntimeKit.FileBasedConfiguration -@_spi(FileBasedConfig) public typealias CRTFileBasedConfigurationSection = AwsCommonRuntimeKit.FileBasedConfiguration.Section -@_spi(FileBasedConfig) public typealias CRTFileBasedConfigurationSectionType = AwsCommonRuntimeKit.FileBasedConfiguration.SectionType -@_spi(FileBasedConfig) public typealias CRTFileBasedConfigurationProperty = AwsCommonRuntimeKit.FileBasedConfiguration.Section.Property +@_spi(FileBasedConfig) public typealias CRTFileBasedConfiguration = + AwsCommonRuntimeKit.FileBasedConfiguration +@_spi(FileBasedConfig) public typealias CRTFileBasedConfigurationSection = + AwsCommonRuntimeKit.FileBasedConfiguration.Section +@_spi(FileBasedConfig) public typealias CRTFileBasedConfigurationSectionType = + AwsCommonRuntimeKit.FileBasedConfiguration.SectionType +@_spi(FileBasedConfig) public typealias CRTFileBasedConfigurationProperty = + AwsCommonRuntimeKit.FileBasedConfiguration.Section.Property extension CRTFileBasedConfigurationSectionType { init(_ type: FileBasedConfigurationSectionType) { @@ -30,7 +34,8 @@ extension CRTFileBasedConfiguration: FileBasedConfiguration { credentialsFilePath: String? = nil ) throws -> CRTFileBasedConfiguration { let configFilePath = try configFilePath ?? CRTFileBasedConfiguration.resolveConfigPath(sourceType: .config) - let credentialsFilePath = try credentialsFilePath ?? CRTFileBasedConfiguration.resolveConfigPath(sourceType: .credentials) + let credentialsFilePath = try credentialsFilePath ?? + CRTFileBasedConfiguration.resolveConfigPath(sourceType: .credentials) return try CRTFileBasedConfiguration(configFilePath: configFilePath, credentialsFilePath: credentialsFilePath) } diff --git a/Sources/Core/AWSClientRuntime/HttpContextBuilder+Extension.swift b/Sources/Core/AWSClientRuntime/HttpContextBuilder+Extension.swift index 64fcadf24d9..564f2912ebc 100644 --- a/Sources/Core/AWSClientRuntime/HttpContextBuilder+Extension.swift +++ b/Sources/Core/AWSClientRuntime/HttpContextBuilder+Extension.swift @@ -6,6 +6,7 @@ // import ClientRuntime +import struct Foundation.Date extension HttpContext { static let credentialsProvider = AttributeKey(name: "CredentialsProvider") diff --git a/Sources/Core/AWSClientRuntime/Utils.swift b/Sources/Core/AWSClientRuntime/Utils.swift new file mode 100644 index 00000000000..e0481819cb5 --- /dev/null +++ b/Sources/Core/AWSClientRuntime/Utils.swift @@ -0,0 +1,18 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct AwsCommonRuntimeKit.CommonRuntimeKit + +public enum Utils { + + /// Sets up CRT-related shared resources such as the global allocator, event loops, etc. + /// + /// Calls to CRT functions may crash the SDK if `CommonRuntimeKit.initialize()` is not called first. + /// + /// This function may safely be called multiple times. + public static func setupCRT() { CommonRuntimeKit.initialize() } +} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSServiceConfig.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSServiceConfig.kt index a05ca3c8fe4..fef19f85b12 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSServiceConfig.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSServiceConfig.kt @@ -8,7 +8,6 @@ package software.amazon.smithy.aws.swift.codegen import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.ShapeType import software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait -import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ConfigField @@ -28,245 +27,30 @@ const val REGION_RESOLVER = "regionResolver" const val SIGNING_REGION_CONFIG_NAME = "signingRegion" const val USE_FIPS_CONFIG_NAME = "useFIPS" const val USE_DUAL_STACK_CONFIG_NAME = "useDualStack" -const val RUNTIME_CONFIG_NAME = "runtimeConfig" const val ENDPOINT_CONFIG_NAME = "endpoint" -const val FILE_BASED_CONFIG_LOCAL_NAME = "fileBasedConfig" - -val runtimeConfig = ConfigField( - RUNTIME_CONFIG_NAME, - type = ClientRuntimeTypes.Core.SDKRuntimeConfiguration, - propFormatter = "\$T" -) - class AWSServiceConfig(writer: SwiftWriter, val ctx: ProtocolGenerator.GenerationContext) : - ServiceConfig(writer, ctx.symbolProvider.toSymbol(ctx.service).name) { - - override val typesToConformConfigTo: List - get() = listOf(AWSClientRuntimeTypes.Core.AWSClientConfiguration) + ServiceConfig(writer, ctx.symbolProvider.toSymbol(ctx.service).name, ctx.service.sdkId) { override fun renderInitializers(serviceSymbol: Symbol) { - val runtimeConfigs = sdkRuntimeConfigProperties() - var otherConfigs = otherRuntimeConfigProperties() val serviceConfigs = serviceConfigProperties() - - // aws configs including service specific configs - var awsConfigs = (otherConfigs + serviceConfigs + runtimeConfig).sortedBy { it.memberName } - - renderAsyncInitializer(awsConfigs) - writer.write("") - renderSyncInitializer(awsConfigs) - writer.write("") - renderDesignatedInitializer(runtimeConfigs, awsConfigs) - writer.write("") - - // partitionID computed var - writer.openBlock("public var partitionID: String? {", "}") { - writer.write("return \"\$L - \\(region ?? \"\")\"", serviceName) - } - } - - fun renderDesignatedInitializer( - runtimeConfigs: List, - awsConfigs: List - ) { - writer.writeDocs("Internal designated init") - writer.writeDocs("All convenience inits should call this") - writer.openBlock("public init(", ") throws {") { - awsConfigs.forEachIndexed { index, config -> - when (config.memberName) { - // Skip regionResolver since region is required - REGION_RESOLVER -> {} - - // We'll always do the runtimeConfig last, so skip here - RUNTIME_CONFIG_NAME -> {} - - CREDENTIALS_PROVIDER_CONFIG_NAME -> { - // Render non-optional - writer.write("\$L: \$N,", config.memberName, config.type) - } - else -> { - writer.write("\$L: \$T,", config.memberName, config.type) - } - } - } - - writer.write("\$L: \$T", RUNTIME_CONFIG_NAME, ClientRuntimeTypes.Core.SDKRuntimeConfiguration) - } - writer.indent() - - // Resolve the runtime config - // let runtimeConfig = try runtimeConfig ?? ClientRuntime.DefaultSDKRuntimeConfiguration("S3Client") - writer.write("let \$L = try \$L ?? \$N(\"\$L\")", RUNTIME_CONFIG_NAME, RUNTIME_CONFIG_NAME, ClientRuntimeTypes.Core.DefaultSDKRuntimeConfiguration, serviceName) - writer.write("") - - // Resolve the signing region - writer.write("let resolvedSigningRegion = \$L ?? \$L", SIGNING_REGION_CONFIG_NAME, REGION_CONFIG_NAME) - writer.write("") - - // Resolve the endpointsResolver - writer.write("let resolvedEndpointsResolver = try \$L ?? \$L()", ENDPOINT_RESOLVER, AWSServiceTypes.DefaultEndpointResolver) - writer.write("") - - awsConfigs.forEach { - when (it.memberName) { - ENDPOINT_RESOLVER -> { - writer.write("self.\$L = resolvedEndpointsResolver", it.memberName) - } - - REGION_RESOLVER -> { - writer.write("// TODO: Remove region resolver. Region must already be resolved and there is no point in storing the resolver.") - writer.write("self.\$L = nil", it.memberName) - } - - SIGNING_REGION_CONFIG_NAME -> { - writer.write("self.\$L = resolvedSigningRegion", it.memberName) - } - - RUNTIME_CONFIG_NAME -> { - // pass, runtime config is not a config field, but its individual configs are stored - } - - else -> { - writer.write("self.\$L = \$L", it.memberName, it.memberName) - } - } - } - - // Handle the runtime config properties - runtimeConfigs.forEach { - writer.write("self.\$L = \$L.\$L", it.memberName, RUNTIME_CONFIG_NAME, it.memberName) - } - - writer.dedent().write("}") - } - - fun renderAsyncInitializer( - awsConfigs: List - ) { - writer.writeDocs("Creates a configuration asynchronously") - writer.openBlock("public convenience init(", ") async throws {") { - awsConfigs.forEachIndexed { index, config -> - val terminator = if (index != awsConfigs.lastIndex) ", " else "" - writer.write("${config.memberName}: ${config.paramFormatter}$terminator", config.type) - } - } - writer.indent() - - // Create our file based config - writer.write("let \$L = try await CRTFileBasedConfiguration.makeAsync()", FILE_BASED_CONFIG_LOCAL_NAME) - writer.write("") - - // Resolve the region resolver - writer.write("let resolvedRegionResolver = try \$L ?? DefaultRegionResolver { _, _ in \$L }", REGION_RESOLVER, FILE_BASED_CONFIG_LOCAL_NAME) - writer.write("") - - // Resolve the region - writer.write("let resolvedRegion: String?") - writer.openBlock("if let \$L = \$L {", "} else {", REGION_CONFIG_NAME, REGION_CONFIG_NAME) { - writer.write("resolvedRegion = \$L", REGION_CONFIG_NAME) - } - writer.indent() - writer.write("resolvedRegion = await resolvedRegionResolver.resolveRegion()") - writer.dedent() - writer.write("}") - writer.write("") - - // Resolve the credentials provider - writer.write("let resolvedCredentialsProvider: \$N", AWSClientRuntimeTypes.Core.CredentialsProviding) - writer.openBlock("if let \$L = \$L {", "} else {", CREDENTIALS_PROVIDER_CONFIG_NAME, CREDENTIALS_PROVIDER_CONFIG_NAME) { - writer.write("resolvedCredentialsProvider = \$L", CREDENTIALS_PROVIDER_CONFIG_NAME) - } - writer.indent() - writer.write("resolvedCredentialsProvider = try DefaultChainCredentialsProvider(fileBasedConfig: \$L)", FILE_BASED_CONFIG_LOCAL_NAME) - writer.dedent() - writer.write("}") - writer.write("") - - writer.openBlock("try self.init(", ")") { - awsConfigs.forEach { - when (it.memberName) { - CREDENTIALS_PROVIDER_CONFIG_NAME -> { - writer.write("\$L: resolvedCredentialsProvider,", it.memberName) - } - - REGION_CONFIG_NAME -> { - writer.write("\$L: resolvedRegion,", it.memberName) - } - - // Skip, region should already be resolved - REGION_RESOLVER -> {} - - // Skip, we'll always do runtime config last - RUNTIME_CONFIG_NAME -> {} - - else -> { - writer.write("\$L: \$L,", it.memberName, it.memberName) - } + writer.openBlock("extension \$L {", "}", clientName) { + writer.write("public typealias \$LConfiguration = AWSClientConfiguration", clientName) + writer.write("") + writer.openBlock("public struct ServiceSpecificConfiguration: AWSServiceSpecificConfiguration {", "}") { + writer.write("public typealias AWSServiceEndpointResolver = EndpointResolver") + writer.write("") + writer.write("public var serviceName: String { \$S }", serviceName) + writer.write("public var clientName: String { \$S }", clientName) + serviceConfigs.forEach { config -> + writer.write("public var \$L: ${config.propFormatter}", config.memberName, config.type) } - } - writer.write("\$L: \$L", RUNTIME_CONFIG_NAME, RUNTIME_CONFIG_NAME) - } - - writer.dedent().write("}") - } - - fun renderSyncInitializer( - awsConfigs: List - ) { - writer.openBlock("public convenience init(", ") throws {") { - writer.write("\$L: \$N,", REGION_CONFIG_NAME, SwiftTypes.String) - - awsConfigs.forEachIndexed { index, config -> - when (config.memberName) { - // Skip, region is handled above - REGION_CONFIG_NAME -> {} - - // Skip, region is required and so no resolver is needed - REGION_RESOLVER -> {} - - else -> { - val terminator = if (index != awsConfigs.lastIndex) ", " else "" - writer.write("${config.memberName}: ${config.paramFormatter}$terminator", config.type) - } - } - } - } - writer.indent() - - writer.write("let resolvedCredentialsProvider: CredentialsProviding") - writer.openBlock("if let \$L = \$L {", "} else {", CREDENTIALS_PROVIDER_CONFIG_NAME, CREDENTIALS_PROVIDER_CONFIG_NAME) { - writer.write("resolvedCredentialsProvider = \$L", CREDENTIALS_PROVIDER_CONFIG_NAME) - } - writer.indent() - writer.write("let \$L = try CRTFileBasedConfiguration.make()", FILE_BASED_CONFIG_LOCAL_NAME) - writer.write("resolvedCredentialsProvider = try DefaultChainCredentialsProvider(fileBasedConfig: \$L)", FILE_BASED_CONFIG_LOCAL_NAME) - writer.dedent() - writer.write("}") - writer.write("") - - writer.openBlock("try self.init(", ")") { - awsConfigs.forEach { - when (it.memberName) { - CREDENTIALS_PROVIDER_CONFIG_NAME -> { - writer.write("\$L: resolvedCredentialsProvider,", it.memberName) - } - - // Skip, region should already be resolved - REGION_RESOLVER -> {} - - // Skip, we'll always do runtime config last - RUNTIME_CONFIG_NAME -> {} - - else -> { - writer.write("\$L: \$L,", it.memberName, it.memberName) - } + writer.write("") + writer.openBlock("public init(endpointResolver: EndpointResolver? = nil) throws {", "}") { + writer.write("self.endpointResolver = try endpointResolver ?? DefaultEndpointResolver()") } } - writer.write("\$L: \$L", RUNTIME_CONFIG_NAME, RUNTIME_CONFIG_NAME) } - - writer.dedent().write("}") } override fun otherRuntimeConfigProperties(): List { diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGenerator.kt index e0f960cd535..d44d12d142e 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGenerator.kt @@ -73,8 +73,8 @@ class PresignerGenerator : SwiftIntegration { val httpBindingResolver = protocolGenerator.getProtocolHttpBindingResolver(protocolGeneratorContext, protocolGenerator.defaultContentType) writer.openBlock("extension $inputType {", "}") { - writer.openBlock("public func presign(config: \$N, expiration: \$N) async throws -> \$T {", "}", serviceConfig.typeProtocol, FoundationTypes.TimeInterval, SdkHttpRequest) { - writer.write("let serviceName = \"${ctx.settings.sdkId}\"") + writer.openBlock("public func presign(config: \$L, expiration: \$N) async throws -> \$T {", "}", serviceConfig.typeName, FoundationTypes.TimeInterval, SdkHttpRequest) { + writer.write("let serviceName = \$S", ctx.settings.sdkId) writer.write("let input = self") val operationStackName = "operation" for (prop in protocolGenerator.httpProtocolCustomizable.getClientProperties()) { diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/ServiceGeneratorIntegration.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/ServiceGeneratorIntegration.kt deleted file mode 100644 index 05ee4c56e77..00000000000 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/ServiceGeneratorIntegration.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.aws.swift.codegen.customization - -import software.amazon.smithy.aws.swift.codegen.AWSClientRuntimeTypes -import software.amazon.smithy.aws.swift.codegen.AWSServiceConfig -import software.amazon.smithy.swift.codegen.ServiceGenerator -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.SectionWriterBinding -import software.amazon.smithy.swift.codegen.integration.SwiftIntegration - -/** - * Overrides the service client configuration definition to use AWS specific protocol. - */ -class ServiceGeneratorIntegration : SwiftIntegration { - override val sectionWriters: List = - listOf( - SectionWriterBinding(ServiceGenerator.ConfigurationProtocolSectionId) { writer, _ -> - writer.addImport(AWSClientRuntimeTypes.Core.AWSClientConfiguration, "FileBasedConfig") - val protocolGenerationContext = - writer.getContext("protocolGenerationContext") as? ProtocolGenerator.GenerationContext - protocolGenerationContext?.let { - val serviceConfig = AWSServiceConfig(writer, protocolGenerationContext) - val serviceConfigs = serviceConfig.serviceConfigProperties() - writer.openBlock( - "public protocol \$L : \$L {", "}", serviceConfig.typeProtocol, serviceConfig.getTypeInheritance() - ) { - serviceConfigs?.let { fields -> - fields.forEach { - writer.write("var ${it.memberName}: ${it.propFormatter} { get }", it.type) - } - } - } - } - } - ) -} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/presignable/PresignableUrlIntegration.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/presignable/PresignableUrlIntegration.kt index 4096dd462f4..d9dfec08fd7 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/presignable/PresignableUrlIntegration.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/customization/presignable/PresignableUrlIntegration.kt @@ -95,8 +95,8 @@ class PresignableUrlIntegration(private val presignedOperations: Map \$T {", "}", - serviceConfig.typeProtocol, + "public func presignURL(config: \$L, expiration: \$N) async throws -> \$T {", "}", + serviceConfig.typeName, FoundationTypes.TimeInterval, ClientRuntimeTypes.Core.URL ) { diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/middleware/OperationEndpointResolverMiddleware.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/middleware/OperationEndpointResolverMiddleware.kt index aa7f648c403..e203eb0aa91 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/middleware/OperationEndpointResolverMiddleware.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/middleware/OperationEndpointResolverMiddleware.kt @@ -70,7 +70,7 @@ class OperationEndpointResolverMiddleware( } } writer.write("let endpointParams = EndpointParams(${params.joinToString(separator = ", ")})") - val middlewareParamsString = "endpointResolver: config.endpointResolver, endpointParams: endpointParams" + val middlewareParamsString = "endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams" writer.write("$operationStackName.${middlewareStep.stringValue()}.intercept(position: ${position.stringValue()}, middleware: \$N<\$N, \$N>($middlewareParamsString))", AWSServiceTypes.EndpointResolverMiddleware, output, outputError) } @@ -108,10 +108,10 @@ class OperationEndpointResolverMiddleware( clientContextParam != null -> { when { param.defaultValue.isPresent -> { - "config.${param.name.toString().toLowerCamelCase()} ?? ${param.defaultValueLiteral}" + "config.serviceSpecific.${param.name.toString().toLowerCamelCase()} ?? ${param.defaultValueLiteral}" } else -> { - return "config.${param.name.toString().toLowerCamelCase()}" + return "config.serviceSpecific.${param.name.toString().toLowerCamelCase()}" } } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration b/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration index 51f92ecf63e..ea2f756d8d1 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration +++ b/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration @@ -14,7 +14,6 @@ software.amazon.smithy.aws.swift.codegen.customization.presignable.PresignableUr software.amazon.smithy.aws.swift.codegen.customization.DisabledAuth software.amazon.smithy.aws.swift.codegen.PresignerGenerator software.amazon.smithy.aws.swift.codegen.model.AWSClientContextParamsTransformer -software.amazon.smithy.aws.swift.codegen.customization.ServiceGeneratorIntegration software.amazon.smithy.aws.swift.codegen.model.AWSHttpTraitTransformer software.amazon.smithy.aws.swift.codegen.model.AWSEndpointTraitTransformer software.amazon.smithy.aws.swift.codegen.customization.MessageEncoderIntegration \ No newline at end of file diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/EventStreamTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/EventStreamTests.kt index 67478a4d7d6..0b3d4df12ce 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/EventStreamTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/EventStreamTests.kt @@ -214,13 +214,13 @@ extension EventStreamTestClient: EventStreamTestClientProtocol { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware()) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) let endpointParams = EndpointParams() - operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.endpointResolver, endpointParams: endpointParams)) + operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams)) let apiMetadata = AWSClientRuntime.APIMetadata(serviceId: serviceName, version: "1.0.0") operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromEnv(apiMetadata: apiMetadata, frameworkMetadata: config.frameworkMetadata))) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) operation.serializeStep.intercept(position: .after, middleware: TestStreamOpInputBodyMiddleware()) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(retryer: config.retryer)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) let sigv4Config = AWSClientRuntime.SigV4Config(unsignedBody: false, signingAlgorithm: .sigv4) operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config)) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware()) diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/OperationEndpointResolverMiddlewareTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/OperationEndpointResolverMiddlewareTests.kt index a8b824ecff0..bafb7814fdc 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/OperationEndpointResolverMiddlewareTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/OperationEndpointResolverMiddlewareTests.kt @@ -25,8 +25,8 @@ class OperationEndpointResolverMiddlewareTests { guard let region = config.region else { throw SdkError.client(ClientError.unknownError(("Missing required parameter: region"))) } - let endpointParams = EndpointParams(boolBar: true, boolBaz: input.fuzz, boolFoo: config.boolFoo, endpoint: config.endpoint, region: region, stringBar: "some value", stringBaz: input.buzz, stringFoo: config.stringFoo) - operationStack.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.endpointResolver, endpointParams: endpointParams)) + let endpointParams = EndpointParams(boolBar: true, boolBaz: input.fuzz, boolFoo: config.serviceSpecific.boolFoo, endpoint: config.endpoint, region: region, stringBar: "some value", stringBaz: input.buzz, stringFoo: config.serviceSpecific.stringFoo) + operationStack.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams)) """.trimIndent() contents.shouldContainOnlyOnce(expected) } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGeneratorTests.kt index 614fdac156e..9107217d175 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/PresignerGeneratorTests.kt @@ -21,7 +21,7 @@ class PresignerGeneratorTests { import typealias Foundation.TimeInterval extension GetFooInput { - public func presign(config: ExampleClientConfigurationProtocol, expiration: Foundation.TimeInterval) async throws -> ClientRuntime.SdkHttpRequest? { + public func presign(config: ExampleClient.ExampleClientConfiguration, expiration: Foundation.TimeInterval) async throws -> ClientRuntime.SdkHttpRequest? { let serviceName = "example" let input = self let encoder = ClientRuntime.JSONEncoder() @@ -48,10 +48,10 @@ class PresignerGeneratorTests { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware()) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) let endpointParams = EndpointParams() - operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.endpointResolver, endpointParams: endpointParams)) + operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams)) let apiMetadata = AWSClientRuntime.APIMetadata(serviceId: serviceName, version: "1.0.0") operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromEnv(apiMetadata: apiMetadata, frameworkMetadata: config.frameworkMetadata))) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(retryer: config.retryer)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) let sigv4Config = AWSClientRuntime.SigV4Config(expiration: expiration, unsignedBody: false, signingAlgorithm: .sigv4) operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config)) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware()) @@ -79,7 +79,7 @@ class PresignerGeneratorTests { import typealias Foundation.TimeInterval extension PostFooInput { - public func presign(config: ExampleClientConfigurationProtocol, expiration: Foundation.TimeInterval) async throws -> ClientRuntime.SdkHttpRequest? { + public func presign(config: ExampleClient.ExampleClientConfiguration, expiration: Foundation.TimeInterval) async throws -> ClientRuntime.SdkHttpRequest? { let serviceName = "example" let input = self let encoder = ClientRuntime.JSONEncoder() @@ -106,13 +106,13 @@ class PresignerGeneratorTests { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware()) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) let endpointParams = EndpointParams() - operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.endpointResolver, endpointParams: endpointParams)) + operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams)) let apiMetadata = AWSClientRuntime.APIMetadata(serviceId: serviceName, version: "1.0.0") operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromEnv(apiMetadata: apiMetadata, frameworkMetadata: config.frameworkMetadata))) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.SerializableBodyMiddleware(xmlName: "GetFooInput")) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(retryer: config.retryer)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) let sigv4Config = AWSClientRuntime.SigV4Config(expiration: expiration, unsignedBody: false, signingAlgorithm: .sigv4) operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config)) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware()) @@ -140,7 +140,7 @@ class PresignerGeneratorTests { import typealias Foundation.TimeInterval extension PutFooInput { - public func presign(config: ExampleClientConfigurationProtocol, expiration: Foundation.TimeInterval) async throws -> ClientRuntime.SdkHttpRequest? { + public func presign(config: ExampleClient.ExampleClientConfiguration, expiration: Foundation.TimeInterval) async throws -> ClientRuntime.SdkHttpRequest? { let serviceName = "example" let input = self let encoder = ClientRuntime.JSONEncoder() @@ -167,13 +167,13 @@ class PresignerGeneratorTests { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware()) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) let endpointParams = EndpointParams() - operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.endpointResolver, endpointParams: endpointParams)) + operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams)) let apiMetadata = AWSClientRuntime.APIMetadata(serviceId: serviceName, version: "1.0.0") operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromEnv(apiMetadata: apiMetadata, frameworkMetadata: config.frameworkMetadata))) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.SerializableBodyMiddleware(xmlName: "GetFooInput")) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(retryer: config.retryer)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) let sigv4Config = AWSClientRuntime.SigV4Config(expiration: expiration, unsignedBody: false, signingAlgorithm: .sigv4) operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config)) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware()) @@ -196,14 +196,12 @@ class PresignerGeneratorTests { contents.shouldSyntacticSanityCheck() val expectedContents = """ - // Code generated by smithy-swift-codegen. DO NOT EDIT! - import AWSClientRuntime import ClientRuntime import typealias Foundation.TimeInterval - + extension PutObjectInput { - public func presign(config: S3ClientConfigurationProtocol, expiration: Foundation.TimeInterval) async throws -> ClientRuntime.SdkHttpRequest? { + public func presign(config: S3Client.S3ClientConfiguration, expiration: Foundation.TimeInterval) async throws -> ClientRuntime.SdkHttpRequest? { let serviceName = "S3" let input = self let encoder = ClientRuntime.JSONEncoder() @@ -230,13 +228,13 @@ class PresignerGeneratorTests { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware()) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) let endpointParams = EndpointParams() - operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.endpointResolver, endpointParams: endpointParams)) + operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams)) let apiMetadata = AWSClientRuntime.APIMetadata(serviceId: serviceName, version: "1.0.0") operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromEnv(apiMetadata: apiMetadata, frameworkMetadata: config.frameworkMetadata))) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/json")) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.SerializableBodyMiddleware(xmlName: "PutObjectInput")) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(retryer: config.retryer)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) let sigv4Config = AWSClientRuntime.SigV4Config(useDoubleURIEncode: false, shouldNormalizeURIPath: false, expiration: expiration, signedBodyHeader: .contentSha256, unsignedBody: false, signingAlgorithm: .sigv4) operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config)) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware()) diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt index 4f56b2acc88..9317d2077f9 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt @@ -84,12 +84,12 @@ class RestJsonProtocolGeneratorTests { public class ExampleClient { public static let clientName = "ExampleClient" let client: ClientRuntime.SdkHttpClient - let config: ExampleClientConfigurationProtocol + let config: ExampleClient.ExampleClientConfiguration let serviceName = "Example" let encoder: ClientRuntime.RequestEncoder let decoder: ClientRuntime.ResponseDecoder - public init(config: ExampleClientConfigurationProtocol) { + public init(config: ExampleClient.ExampleClientConfiguration) { client = ClientRuntime.SdkHttpClient(engine: config.httpClientEngine, config: config.httpClientConfiguration) let encoder = ClientRuntime.JSONEncoder() encoder.dateEncodingStrategy = .secondsSince1970 @@ -103,153 +103,28 @@ class RestJsonProtocolGeneratorTests { } public convenience init(region: Swift.String) throws { - let config = try ExampleClientConfiguration(region: region) + let config = try ExampleClient.ExampleClientConfiguration(region: region) self.init(config: config) } public convenience init() async throws { - let config = try await ExampleClientConfiguration() + let config = try await ExampleClient.ExampleClientConfiguration() self.init(config: config) } + } - public class ExampleClientConfiguration: ExampleClientConfigurationProtocol { - public var clientLogMode: ClientRuntime.ClientLogMode - public var decoder: ClientRuntime.ResponseDecoder? - public var encoder: ClientRuntime.RequestEncoder? - public var httpClientConfiguration: ClientRuntime.HttpClientConfiguration - public var httpClientEngine: ClientRuntime.HttpClientEngine - public var idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator - public var logger: ClientRuntime.LogAgent - public var retryer: ClientRuntime.SDKRetryer + extension ExampleClient { + public typealias ExampleClientConfiguration = AWSClientConfiguration - public var credentialsProvider: AWSClientRuntime.CredentialsProviding - public var endpoint: Swift.String? - public var frameworkMetadata: AWSClientRuntime.FrameworkMetadata? - public var region: Swift.String? - public var regionResolver: AWSClientRuntime.RegionResolver? - public var signingRegion: Swift.String? - public var useDualStack: Swift.Bool? - public var useFIPS: Swift.Bool? + public struct ServiceSpecificConfiguration: AWSServiceSpecificConfiguration { + public typealias AWSServiceEndpointResolver = EndpointResolver + public var serviceName: String { "Example" } + public var clientName: String { "ExampleClient" } public var endpointResolver: EndpointResolver - /// Creates a configuration asynchronously - public convenience init( - credentialsProvider: AWSClientRuntime.CredentialsProviding? = nil, - endpoint: Swift.String? = nil, - endpointResolver: EndpointResolver? = nil, - frameworkMetadata: AWSClientRuntime.FrameworkMetadata? = nil, - region: Swift.String? = nil, - regionResolver: AWSClientRuntime.RegionResolver? = nil, - runtimeConfig: ClientRuntime.SDKRuntimeConfiguration? = nil, - signingRegion: Swift.String? = nil, - useDualStack: Swift.Bool? = nil, - useFIPS: Swift.Bool? = nil - ) async throws { - let fileBasedConfig = try await CRTFileBasedConfiguration.makeAsync() - - let resolvedRegionResolver = try regionResolver ?? DefaultRegionResolver { _, _ in fileBasedConfig } - - let resolvedRegion: String? - if let region = region { - resolvedRegion = region - } else { - resolvedRegion = await resolvedRegionResolver.resolveRegion() - } - - let resolvedCredentialsProvider: AWSClientRuntime.CredentialsProviding - if let credentialsProvider = credentialsProvider { - resolvedCredentialsProvider = credentialsProvider - } else { - resolvedCredentialsProvider = try DefaultChainCredentialsProvider(fileBasedConfig: fileBasedConfig) - } - - try self.init( - credentialsProvider: resolvedCredentialsProvider, - endpoint: endpoint, - endpointResolver: endpointResolver, - frameworkMetadata: frameworkMetadata, - region: resolvedRegion, - signingRegion: signingRegion, - useDualStack: useDualStack, - useFIPS: useFIPS, - runtimeConfig: runtimeConfig - ) - } - - public convenience init( - region: Swift.String, - credentialsProvider: AWSClientRuntime.CredentialsProviding? = nil, - endpoint: Swift.String? = nil, - endpointResolver: EndpointResolver? = nil, - frameworkMetadata: AWSClientRuntime.FrameworkMetadata? = nil, - runtimeConfig: ClientRuntime.SDKRuntimeConfiguration? = nil, - signingRegion: Swift.String? = nil, - useDualStack: Swift.Bool? = nil, - useFIPS: Swift.Bool? = nil - ) throws { - let resolvedCredentialsProvider: CredentialsProviding - if let credentialsProvider = credentialsProvider { - resolvedCredentialsProvider = credentialsProvider - } else { - let fileBasedConfig = try CRTFileBasedConfiguration.make() - resolvedCredentialsProvider = try DefaultChainCredentialsProvider(fileBasedConfig: fileBasedConfig) - } - - try self.init( - credentialsProvider: resolvedCredentialsProvider, - endpoint: endpoint, - endpointResolver: endpointResolver, - frameworkMetadata: frameworkMetadata, - region: region, - signingRegion: signingRegion, - useDualStack: useDualStack, - useFIPS: useFIPS, - runtimeConfig: runtimeConfig - ) - } - - /// Internal designated init - /// All convenience inits should call this - public init( - credentialsProvider: AWSClientRuntime.CredentialsProviding, - endpoint: Swift.String?, - endpointResolver: EndpointResolver?, - frameworkMetadata: AWSClientRuntime.FrameworkMetadata?, - region: Swift.String?, - signingRegion: Swift.String?, - useDualStack: Swift.Bool?, - useFIPS: Swift.Bool?, - runtimeConfig: ClientRuntime.SDKRuntimeConfiguration? - ) throws { - let runtimeConfig = try runtimeConfig ?? ClientRuntime.DefaultSDKRuntimeConfiguration("ExampleClient") - - let resolvedSigningRegion = signingRegion ?? region - - let resolvedEndpointsResolver = try endpointResolver ?? DefaultEndpointResolver() - - self.credentialsProvider = credentialsProvider - self.endpoint = endpoint - self.endpointResolver = resolvedEndpointsResolver - self.frameworkMetadata = frameworkMetadata - self.region = region - // TODO: Remove region resolver. Region must already be resolved and there is no point in storing the resolver. - self.regionResolver = nil - self.signingRegion = resolvedSigningRegion - self.useDualStack = useDualStack - self.useFIPS = useFIPS - self.clientLogMode = runtimeConfig.clientLogMode - self.decoder = runtimeConfig.decoder - self.encoder = runtimeConfig.encoder - self.httpClientConfiguration = runtimeConfig.httpClientConfiguration - self.httpClientEngine = runtimeConfig.httpClientEngine - self.idempotencyTokenGenerator = runtimeConfig.idempotencyTokenGenerator - self.logger = runtimeConfig.logger - self.retryer = runtimeConfig.retryer - } - - public var partitionID: String? { - return "ExampleClient - \(region ?? "")" + public init(endpointResolver: EndpointResolver? = nil) throws { + self.endpointResolver = try endpointResolver ?? DefaultEndpointResolver() } } } diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSQueryOperationStackTest.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSQueryOperationStackTest.kt index c7eedda265d..bf8832f51ed 100644 --- a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSQueryOperationStackTest.kt +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/awsquery/AWSQueryOperationStackTest.kt @@ -21,6 +21,7 @@ class AWSQueryOperationStackTest { contents.shouldSyntacticSanityCheck() val expectedContents = """ +extension QueryProtocolClient: QueryProtocolClientProtocol { public func noInputAndOutput(input: NoInputAndOutputInput) async throws -> NoInputAndOutputOutputResponse { let context = ClientRuntime.HttpContextBuilder() @@ -39,18 +40,20 @@ class AWSQueryOperationStackTest { operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware()) operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) let endpointParams = EndpointParams() - operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.endpointResolver, endpointParams: endpointParams)) + operation.buildStep.intercept(position: .before, middleware: EndpointResolverMiddleware(endpointResolver: config.serviceSpecific.endpointResolver, endpointParams: endpointParams)) let apiMetadata = AWSClientRuntime.APIMetadata(serviceId: serviceName, version: "1.0.0") operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromEnv(apiMetadata: apiMetadata, frameworkMetadata: config.frameworkMetadata))) operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.SerializableBodyMiddleware(xmlName: "NoInputAndOutputInput")) operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware(contentType: "application/x-www-form-urlencoded")) operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware()) - operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(retryer: config.retryer)) + operation.finalizeStep.intercept(position: .after, middleware: ClientRuntime.RetryMiddleware(options: config.retryStrategyOptions)) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware()) operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) let result = try await operation.handleMiddleware(context: context, input: input, next: client.getHandler()) return result - }""" + } + +}""" contents.shouldContainOnlyOnce(expectedContents) }