diff --git a/IntegrationTests/Package.swift b/IntegrationTests/Package.swift index 8e13752ddc9..539fbfa6c64 100644 --- a/IntegrationTests/Package.swift +++ b/IntegrationTests/Package.swift @@ -46,6 +46,7 @@ let package = Package( private var integrationTestTargets: [Target] { let integrationTests = [ "AWSCloudFrontKeyValueStore", + "AWSDynamoDB", "AWSEC2", "AWSECS", "AWSEventBridge", @@ -112,3 +113,4 @@ private func integrationTestTarget(_ name: String) -> Target { resources: [.process("Resources")] ) } + diff --git a/IntegrationTests/Services/AWSDynamoDBIntegrationTests/AccountIDEndpointModeTests.swift b/IntegrationTests/Services/AWSDynamoDBIntegrationTests/AccountIDEndpointModeTests.swift new file mode 100644 index 00000000000..68d9571de8b --- /dev/null +++ b/IntegrationTests/Services/AWSDynamoDBIntegrationTests/AccountIDEndpointModeTests.swift @@ -0,0 +1,149 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +import AWSDynamoDB +import SmithyTestUtil +import SmithyIdentity +import enum AWSClientRuntime.AccountIDEndpointMode +import enum ClientRuntime.EndpointError + +final class AccountIDEndpointModeTests: XCTestCase { + private let accountID = "0123456789" + + // MARK: - Tests + + // MARK: nil + + func test_nilMode_prefersByDefault() async throws { + let subject = try await subject(accountIDEndpointMode: nil, setAccountID: true) + + do { + _ = try await subject.getItem(input: GetItemInput()) + XCTFail("Request should have thrown") + } catch TestCheckError.actual(let request) { + XCTAssert(request.endpoint.host.contains(accountID)) + } catch { + XCTFail("Request should have succeeded, threw error: \(error)") + } + } + + func test_nilMode_createsEndpointWithoutAccountID() async throws { + let subject = try await subject(accountIDEndpointMode: nil, setAccountID: false) + + do { + _ = try await subject.getItem(input: GetItemInput()) + XCTFail("Request should have thrown") + } catch TestCheckError.actual(let request) { + XCTAssertFalse(request.endpoint.host.contains(accountID)) + } catch { + XCTFail("Request failed on unexpected error") + } + } + + // MARK: preferred + + func test_preferredMode_prefersByDefault() async throws { + let subject = try await subject(accountIDEndpointMode: .preferred, setAccountID: true) + + do { + _ = try await subject.getItem(input: GetItemInput()) + XCTFail("Request should have thrown") + } catch TestCheckError.actual(let request) { + XCTAssert(request.endpoint.host.contains(accountID)) + } catch { + XCTFail("Request should have succeeded, threw error: \(error)") + } + } + + func test_preferredMode_createsEndpointWithoutAccountID() async throws { + let subject = try await subject(accountIDEndpointMode: .preferred, setAccountID: false) + + do { + _ = try await subject.getItem(input: GetItemInput()) + XCTFail("Request should have thrown") + } catch TestCheckError.actual(let request) { + XCTAssertFalse(request.endpoint.host.contains(accountID)) + } catch { + XCTFail("Request failed on unexpected error") + } + } + + // MARK: required + + func test_requiredMode_createsEndpointWithAccountID() async throws { + let subject = try await subject(accountIDEndpointMode: .required, setAccountID: true) + + do { + _ = try await subject.getItem(input: GetItemInput()) + XCTFail("Request should have thrown") + } catch TestCheckError.actual(let request) { + XCTAssert(request.endpoint.host.contains(accountID)) + } catch { + XCTFail("Request should have succeeded, threw error: \(error)") + } + } + + func test_requiredMode_failsWhenRequiredButNotPresent() async throws { + let subject = try await subject(accountIDEndpointMode: .required, setAccountID: false) + + do { + _ = try await subject.getItem(input: GetItemInput()) + XCTFail("Request should have thrown") + } catch EndpointError.unresolved { + // No action, test succeeded + } catch { + XCTFail("Request failed on unexpected error") + } + } + + // MARK: disabled + + func test_disabledMode_createsEndpointWithoutAccountIDWhenNil() async throws { + let subject = try await subject(accountIDEndpointMode: .disabled, setAccountID: false) + + do { + _ = try await subject.getItem(input: GetItemInput()) + XCTFail("Request should have thrown") + } catch TestCheckError.actual(let request) { + XCTAssertFalse(request.endpoint.host.contains(accountID)) + } catch { + XCTFail("Request failed on unexpected error") + } + } + + func test_disabledMode_createsEndpointWithoutAccountIDWhenSupplied() async throws { + let subject = try await subject(accountIDEndpointMode: .disabled, setAccountID: true) + + do { + _ = try await subject.getItem(input: GetItemInput()) + XCTFail("Request should have thrown") + } catch TestCheckError.actual(let request) { + XCTAssertFalse(request.endpoint.host.contains(accountID)) + } catch { + XCTFail("Request failed on unexpected error") + } + } + + // MARK: - Private methods + + private func subject( + accountIDEndpointMode: AccountIDEndpointMode?, + setAccountID: Bool + ) async throws -> DynamoDBClient { + let accountID = setAccountID ? self.accountID : nil + let credentials = AWSCredentialIdentity(accessKey: "abc", secret: "def", accountID: accountID) + let resolver = try StaticAWSCredentialIdentityResolver(credentials) + let config = try await DynamoDBClient.Config( + awsCredentialIdentityResolver: resolver, + region: "us-east-1", + accountIdEndpointMode: accountIDEndpointMode, + httpClientEngine: ProtocolTestClient() + ) + return DynamoDBClient(config: config) + } +} diff --git a/IntegrationTests/Services/AWSDynamoDBIntegrationTests/Resources/.gitkeep b/IntegrationTests/Services/AWSDynamoDBIntegrationTests/Resources/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/IntegrationTests/XCTestPlans/AWSIntegrationTestsOnCI.xctestplan b/IntegrationTests/XCTestPlans/AWSIntegrationTestsOnCI.xctestplan index eb8e344e930..24326dba6d3 100644 --- a/IntegrationTests/XCTestPlans/AWSIntegrationTestsOnCI.xctestplan +++ b/IntegrationTests/XCTestPlans/AWSIntegrationTestsOnCI.xctestplan @@ -17,43 +17,43 @@ { "target" : { "containerPath" : "container:", - "identifier" : "AWSCognitoIdentityIntegrationTests", - "name" : "AWSCognitoIdentityIntegrationTests" + "identifier" : "AWSSQSIntegrationTests", + "name" : "AWSSQSIntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSSTSIntegrationTests", - "name" : "AWSSTSIntegrationTests" + "identifier" : "AWSKinesisIntegrationTests", + "name" : "AWSKinesisIntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSTranscribeStreamingIntegrationTests", - "name" : "AWSTranscribeStreamingIntegrationTests" + "identifier" : "AWSEC2IntegrationTests", + "name" : "AWSEC2IntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSECSIntegrationTests", - "name" : "AWSECSIntegrationTests" + "identifier" : "AWSSTSIntegrationTests", + "name" : "AWSSTSIntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSGlacierIntegrationTests", - "name" : "AWSGlacierIntegrationTests" + "identifier" : "AWSDynamoDBIntegrationTests", + "name" : "AWSDynamoDBIntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSMediaConvertIntegrationTests", - "name" : "AWSMediaConvertIntegrationTests" + "identifier" : "AWSECSIntegrationTests", + "name" : "AWSECSIntegrationTests" } }, { @@ -66,43 +66,50 @@ { "target" : { "containerPath" : "container:", - "identifier" : "AWSSQSIntegrationTests", - "name" : "AWSSQSIntegrationTests" + "identifier" : "AWSS3IntegrationTests", + "name" : "AWSS3IntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSEventBridgeIntegrationTests", - "name" : "AWSEventBridgeIntegrationTests" + "identifier" : "AWSCognitoIdentityIntegrationTests", + "name" : "AWSCognitoIdentityIntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSEC2IntegrationTests", - "name" : "AWSEC2IntegrationTests" + "identifier" : "AWSTranscribeStreamingIntegrationTests", + "name" : "AWSTranscribeStreamingIntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSKinesisIntegrationTests", - "name" : "AWSKinesisIntegrationTests" + "identifier" : "AWSRoute53IntegrationTests", + "name" : "AWSRoute53IntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSRoute53IntegrationTests", - "name" : "AWSRoute53IntegrationTests" + "identifier" : "AWSGlacierIntegrationTests", + "name" : "AWSGlacierIntegrationTests" } }, { "target" : { "containerPath" : "container:", - "identifier" : "AWSS3IntegrationTests", - "name" : "AWSS3IntegrationTests" + "identifier" : "AWSMediaConvertIntegrationTests", + "name" : "AWSMediaConvertIntegrationTests" + } + }, + { + "target" : { + "containerPath" : "container:", + "identifier" : "AWSEventBridgeIntegrationTests", + "name" : "AWSEventBridgeIntegrationTests" } } ], diff --git a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/AWSClientConfigDefaultsProvider.swift b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/AWSClientConfigDefaultsProvider.swift index a404faef401..3d7a2678a9f 100644 --- a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/AWSClientConfigDefaultsProvider.swift +++ b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/AWSClientConfigDefaultsProvider.swift @@ -135,4 +135,19 @@ public class AWSClientConfigDefaultsProvider { rateLimitingMode: resolvedRateLimitingMode ) } + + public static func accountIDEndpointMode( + _ accountIDEndpointMode: AccountIDEndpointMode? = nil + ) throws -> AccountIDEndpointMode { + let fileBasedConfig = try CRTFileBasedConfiguration.make() + if let accountIDEndpointMode { + return accountIDEndpointMode + } else { + return AWSEndpointConfig.accountIDEndpointMode( + configValue: accountIDEndpointMode, + profileName: nil, + fileBasedConfig: fileBasedConfig + ) + } + } } diff --git a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/AWSEndpointConfig.swift b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/AWSEndpointConfig.swift new file mode 100644 index 00000000000..e14fac56d08 --- /dev/null +++ b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/AWSEndpointConfig.swift @@ -0,0 +1,26 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(FileBasedConfig) import AWSSDKCommon + +public enum AWSEndpointConfig { + + static func accountIDEndpointMode( + configValue: AccountIDEndpointMode?, + profileName: String?, + fileBasedConfig: FileBasedConfiguration + ) -> AccountIDEndpointMode { + FieldResolver( + configValue: configValue, + envVarName: "AWS_ACCOUNT_ID_ENDPOINT_MODE", + configFieldName: "account_id_endpoint_mode", + fileBasedConfig: fileBasedConfig, + profileName: profileName, + converter: { AccountIDEndpointMode(rawValue: $0) } + ).value ?? .preferred + } +} diff --git a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/AccountIDEndpointMode.swift b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/AccountIDEndpointMode.swift new file mode 100644 index 00000000000..8ac03ca6ebf --- /dev/null +++ b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/AccountIDEndpointMode.swift @@ -0,0 +1,27 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +/// Determines how & whether an account ID-based endpoint will be used for requests. +public enum AccountIDEndpointMode: String, Equatable { + // Note : these case names match string values for the accountIdEndpointMode endpoint param. + // Do not rename them + + /// An account ID-based endpoint will be used if an account ID is available. + /// + /// This is the default mode. + case preferred // the default case + + /// An account ID-based endpoint will never be used. + /// + /// The request will fail if a non-account ID-based endpoint is not available. + case disabled + + /// An account ID-based endpoint will always be used. + /// + /// The request will fail if an account ID-based endpoint cannot be constructed. + case required +} diff --git a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/Context+AccountIDEndpointMode.swift b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/Context+AccountIDEndpointMode.swift new file mode 100644 index 00000000000..995b71ee25f --- /dev/null +++ b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/Context+AccountIDEndpointMode.swift @@ -0,0 +1,30 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Smithy.Attributes +import struct Smithy.AttributeKey +import class Smithy.Context +import class Smithy.ContextBuilder + +public extension Context { + + var accountIDEndpointMode: AccountIDEndpointMode? { + get { get(key: accountIDEndpointModeKey) } + set { set(key: accountIDEndpointModeKey, value: newValue) } + } +} + +public extension ContextBuilder { + + @discardableResult + func withAccountIDEndpointMode(value: AccountIDEndpointMode?) -> Self { + attributes.set(key: accountIDEndpointModeKey, value: value) + return self + } +} + +private let accountIDEndpointModeKey = AttributeKey(name: "AccountIDEndpointMode") diff --git a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/Context+ResolvedEndpoint.swift b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/Context+ResolvedEndpoint.swift new file mode 100644 index 00000000000..36e5cdae35e --- /dev/null +++ b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/Context+ResolvedEndpoint.swift @@ -0,0 +1,21 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import struct Smithy.Attributes +import struct Smithy.AttributeKey +import class Smithy.Context +import struct SmithyHTTPAPI.Endpoint + +public extension Context { + + var resolvedEndpoint: Endpoint? { + get { get(key: resolvedEndpointKey) } + set { set(key: resolvedEndpointKey, value: newValue) } + } +} + +private let resolvedEndpointKey = AttributeKey(name: "ResolvedEndpoint") diff --git a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/EndpointResolverMiddleware.swift b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/EndpointResolverMiddleware.swift index 067c3ef0b16..f15c3631ae1 100644 --- a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/EndpointResolverMiddleware.swift +++ b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/Endpoints/EndpointResolverMiddleware.swift @@ -18,27 +18,26 @@ import enum ClientRuntime.EndpointsAuthScheme import protocol ClientRuntime.EndpointsAuthSchemeResolver import protocol ClientRuntime.EndpointsRequestContextProviding -public struct EndpointResolverMiddleware { +@_spi(AWSEndpointResolverMiddleware) +public struct AWSEndpointResolverMiddleware { public let id: Swift.String = "EndpointResolverMiddleware" - - let endpointResolverBlock: (Params) throws -> Endpoint - - let endpointParams: Params - + let paramsBlock: (Context) throws -> Params + let resolverBlock: (Params) throws -> Endpoint let authSchemeResolver: ClientRuntime.EndpointsAuthSchemeResolver public init( - endpointResolverBlock: @escaping (Params) throws -> Endpoint, - endpointParams: Params, + paramsBlock: @escaping (Context) throws -> Params, + resolverBlock: @escaping (Params) throws -> Endpoint, authSchemeResolver: EndpointsAuthSchemeResolver = DefaultEndpointsAuthSchemeResolver() ) { - self.endpointResolverBlock = endpointResolverBlock - self.endpointParams = endpointParams + self.paramsBlock = paramsBlock + self.resolverBlock = resolverBlock self.authSchemeResolver = authSchemeResolver } } -extension EndpointResolverMiddleware: ApplyEndpoint { +extension AWSEndpointResolverMiddleware: ApplyEndpoint { + public func apply( request: SmithyHTTPAPI.HTTPRequest, selectedAuthScheme: SelectedAuthScheme?, @@ -46,7 +45,10 @@ extension EndpointResolverMiddleware: ApplyEndpoint { ) async throws -> SmithyHTTPAPI.HTTPRequest { let builder = request.toBuilder() - let endpoint = try endpointResolverBlock(endpointParams) + let endpoint = try resolverBlock(paramsBlock(attributes)) + + // Put endpoint into context for use in business metrics + attributes.resolvedEndpoint = endpoint var signingName: String? var signingAlgorithm: String? diff --git a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/UserAgent/BusinessMetrics.swift b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/UserAgent/BusinessMetrics.swift index b4d7118c80d..5adab2c286d 100644 --- a/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/UserAgent/BusinessMetrics.swift +++ b/Sources/Core/AWSClientRuntime/Sources/AWSClientRuntime/UserAgent/BusinessMetrics.swift @@ -98,14 +98,39 @@ private func setFlagsIntoContext( case .adaptive: context.businessMetrics = ["RETRY_MODE_ADAPTIVE": "F"] } + // Handle N if let endpoint = config.endpoint, !endpoint.isEmpty { context.businessMetrics = ["ENDPOINT_OVERRIDE": "N"] } + + // Handle O + if let endpoint = context.resolvedEndpoint, let accountID = context.resolvedAWSAccountID, endpoint.host.contains(accountID) { + context.businessMetrics = ["ACCOUNT_ID_ENDPOINT": "O"] + } + + // Handle P, Q, R + if let accountIDEndpointMode = context.accountIDEndpointMode { + switch accountIDEndpointMode { + case .preferred: + context.businessMetrics = ["ACCOUNT_ID_MODE_PREFERRED": "P"] + case .disabled: + context.businessMetrics = ["ACCOUNT_ID_MODE_DISABLED": "Q"] + case .required: + context.businessMetrics = ["ACCOUNT_ID_MODE_REQUIRED": "R"] + } + } + // Handle S if context.selectedAuthScheme?.schemeID == "aws.auth#sigv4a" { context.businessMetrics = ["SIGV4A_SIGNING": "S"] } + + // Handle T + if context.resolvedAWSAccountID != nil { + context.businessMetrics = ["RESOLVED_ACCOUNT_ID": "T"] + } + // Handle M if headers.value(for: "smithy-protocol") == "rpc-v2-cbor" { context.businessMetrics = ["PROTOCOL_RPC_V2_CBOR": "M"] diff --git a/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/Endpoints/AWSEndpointConfigTests.swift b/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/Endpoints/AWSEndpointConfigTests.swift new file mode 100644 index 00000000000..f74aa23aa59 --- /dev/null +++ b/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/Endpoints/AWSEndpointConfigTests.swift @@ -0,0 +1,39 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@testable @_spi(FileBasedConfig) import AWSClientRuntime +@_spi(FileBasedConfig) @testable import AWSSDKCommon + +class AWSEndpointConfigTests: XCTestCase { + var fileBasedConfig: FileBasedConfiguration = try! CRTFileBasedConfiguration( + configFilePath: Bundle.module.path(forResource: "aws_endpoint_config_tests", ofType: nil)!, + credentialsFilePath: nil + ) + + // MARK: - accountIdEndpointMode + + func test_accountIdEndpointMode_resolvesAConfigValue() throws { + let subject = AWSEndpointConfig.accountIDEndpointMode(configValue: .required, profileName: nil, fileBasedConfig: fileBasedConfig) + XCTAssertEqual(subject, .required) + } + + func test_accountIdEndpointMode_resolvesDefaultProfile() throws { + let subject = AWSEndpointConfig.accountIDEndpointMode(configValue: nil, profileName: nil, fileBasedConfig: fileBasedConfig) + XCTAssertEqual(subject, .disabled) + } + + func test_accountIdEndpointMode_resolvesSpecifiedProfile() throws { + let subject = AWSEndpointConfig.accountIDEndpointMode(configValue: nil, profileName: "aws-endpoint-config-test", fileBasedConfig: fileBasedConfig) + XCTAssertEqual(subject, .required) + } + + func test_accountIdEndpointMode_defaultsToPreferred() throws { + let subject = AWSEndpointConfig.accountIDEndpointMode(configValue: nil, profileName: "no-such-profile", fileBasedConfig: fileBasedConfig) + XCTAssertEqual(subject, .preferred) + } +} diff --git a/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/Resources/aws_endpoint_config_tests b/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/Resources/aws_endpoint_config_tests new file mode 100644 index 00000000000..5976feaa299 --- /dev/null +++ b/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/Resources/aws_endpoint_config_tests @@ -0,0 +1,5 @@ +[default] +account_id_endpoint_mode = disabled + +[profile aws-endpoint-config-test] +account_id_endpoint_mode = required diff --git a/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/UserAgent/BusinessMetricsTests.swift b/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/UserAgent/BusinessMetricsTests.swift index 7728e718ccd..8110a93f86f 100644 --- a/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/UserAgent/BusinessMetricsTests.swift +++ b/Sources/Core/AWSClientRuntime/Tests/AWSClientRuntimeTests/UserAgent/BusinessMetricsTests.swift @@ -9,6 +9,7 @@ import XCTest import ClientRuntime @testable import AWSClientRuntime import SmithyRetriesAPI +import SmithyHTTPAPI import SmithyHTTPAuthAPI import SmithyHTTPAPI import SmithyIdentity @@ -17,24 +18,24 @@ import Smithy class BusinessMetricsTests: XCTestCase { var context: Context! + var config: UserAgentValuesFromConfig! var headers: Headers! override func setUp() async throws { + config = UserAgentValuesFromConfig(appID: nil, endpoint: nil, awsRetryMode: .standard) context = Context(attributes: Attributes()) headers = Headers() } + // MARK: - Truncation + func test_business_metrics_section_truncation() { context.businessMetrics = ["SHORT_FILLER": "A"] let longMetricValue = String(repeating: "F", count: 1025) context.businessMetrics = ["LONG_FILLER": longMetricValue] - let userAgent = AWSUserAgentMetadata.fromConfigAndContext( - serviceID: "test", - version: "1.0", - config: UserAgentValuesFromConfig(appID: nil, endpoint: nil, awsRetryMode: .standard), - context: context, - headers: headers - ) + + let userAgent = testUserAgent() + // Assert values in context match with values assigned to user agent XCTAssertEqual(userAgent.businessMetrics?.features, context.businessMetrics) // Assert string gets truncated successfully @@ -42,6 +43,8 @@ class BusinessMetricsTests: XCTestCase { XCTAssertEqual(userAgent.businessMetrics?.description, expectedTruncatedString) } + // MARK: - Multiple flags + func test_multiple_flags_in_context() { context.businessMetrics = ["FIRST": "A"] context.businessMetrics = ["SECOND": "B"] @@ -51,15 +54,85 @@ class BusinessMetricsTests: XCTestCase { signingProperties: nil, signer: nil )) - let userAgent = AWSUserAgentMetadata.fromConfigAndContext( + + config = UserAgentValuesFromConfig(appID: nil, endpoint: "test-endpoint", awsRetryMode: .adaptive) + let userAgent = testUserAgent() + + // F comes from retry mode being adaptive & N comes from endpoint override + let expectedString = "m/A,B,F,N,S" + XCTAssertEqual(userAgent.businessMetrics?.description, expectedString) + } + + // MARK: - Account ID in Endpoint + + func test_accountIDInEndpoint_noAccountIDInEndpoint() { + configureContext(host: "dynamodb.us-east-1.amazonaws.com", accountID: "0123456789") + + let userAgent = testUserAgent() + + // E comes from retry mode & T comes from resolving account ID + let expectedString = "m/E,T" + XCTAssertEqual(userAgent.businessMetrics?.description, expectedString) + } + + func test_accountIDInEndpoint_hasAccountIDInEndpoint() { + configureContext(host: "0123456789.dynamodb.us-east-1.amazonaws.com", accountID: "0123456789") + + let userAgent = testUserAgent() + + // E comes from retry mode, O comes from account ID in endpoint, & T comes from resolving account ID + let expectedString = "m/E,O,T" + XCTAssertEqual(userAgent.businessMetrics?.description, expectedString) + } + + // MARK: - AccountIDEndpointMode + + func test_accountIDEndpointMode_recordsPreferredCorrectly() { + context.accountIDEndpointMode = .preferred + + let userAgent = testUserAgent() + + // E comes from retry mode & P comes from preferred account ID endpoint mode + let expectedString = "m/E,P" + XCTAssertEqual(userAgent.businessMetrics?.description, expectedString) + } + + func test_accountIDEndpointMode_recordsDisabledCorrectly() { + context.accountIDEndpointMode = .disabled + + let userAgent = testUserAgent() + + // E comes from retry mode & Q comes from disabled account ID endpoint mode + let expectedString = "m/E,Q" + XCTAssertEqual(userAgent.businessMetrics?.description, expectedString) + } + + func test_accountIDEndpointMode_recordsRequiredCorrectly() { + context.accountIDEndpointMode = .required + + let userAgent = testUserAgent() + + // E comes from retry mode & R comes from required account ID endpoint mode + let expectedString = "m/E,R" + XCTAssertEqual(userAgent.businessMetrics?.description, expectedString) + } + + // MARK: - Private methods + + private func testUserAgent() -> AWSUserAgentMetadata { + AWSUserAgentMetadata.fromConfigAndContext( serviceID: "test", version: "1.0", - config: UserAgentValuesFromConfig(appID: nil, endpoint: "test-endpoint", awsRetryMode: .adaptive), + config: config, context: context, headers: headers ) - // F comes from retry mode being adaptive & N comes from endpoint override - let expectedString = "m/A,B,F,N,S" - XCTAssertEqual(userAgent.businessMetrics?.description, expectedString) + } + + private func configureContext(host: String, accountID: String) { + let selectedAuthScheme = SelectedAuthScheme(schemeID: "aws.auth#sigv4", identity: AWSCredentialIdentity(accessKey: "abc", secret: "def", accountID: accountID), signingProperties: Attributes(), signer: nil) + context.selectedAuthScheme = selectedAuthScheme + let uri = URIBuilder().withScheme(.https).withPath("/").withHost(host).build() + context.resolvedEndpoint = Endpoint(uri: uri) } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPProtocolClientGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPProtocolClientGenerator.kt new file mode 100644 index 00000000000..ffe5ab7b1b5 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPProtocolClientGenerator.kt @@ -0,0 +1,39 @@ +package software.amazon.smithy.aws.swift.codegen + +import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable +import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver +import software.amazon.smithy.swift.codegen.integration.HttpProtocolClientGenerator +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.ServiceConfig +import software.amazon.smithy.swift.codegen.middleware.MiddlewareExecutionGenerator +import software.amazon.smithy.swift.codegen.middleware.OperationMiddleware + +class AWSHTTPProtocolClientGenerator( + private val ctx: ProtocolGenerator.GenerationContext, + private val writer: SwiftWriter, + serviceConfig: ServiceConfig, + private val httpBindingResolver: HttpBindingResolver, + private val defaultContentType: String, + private val httpProtocolCustomizable: HTTPProtocolCustomizable, + private val operationMiddleware: OperationMiddleware +) : HttpProtocolClientGenerator(ctx, writer, serviceConfig, httpBindingResolver, defaultContentType, httpProtocolCustomizable, operationMiddleware) { + + override fun makeMiddlewareExecutionGenerator( + ctx: ProtocolGenerator.GenerationContext, + writer: SwiftWriter, + httpBindingResolver: HttpBindingResolver, + httpProtocolCustomizable: HTTPProtocolCustomizable, + operationMiddleware: OperationMiddleware, + operationStackName: String + ): MiddlewareExecutionGenerator { + return AWSMiddlewareExecutionGenerator( + ctx, + writer, + httpBindingResolver, + httpProtocolCustomizable, + operationMiddleware, + operationStackName + ) + } +} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPProtocolCustomizations.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPProtocolCustomizations.kt index 5e03894b2b9..062ff8cb150 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPProtocolCustomizations.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPProtocolCustomizations.kt @@ -67,7 +67,7 @@ abstract class AWSHTTPProtocolCustomizations : DefaultHTTPProtocolCustomizations return AWSHttpProtocolServiceClient(ctx, writer, serviceConfig) } - override val endpointMiddlewareSymbol: Symbol = AWSClientRuntimeTypes.Core.EndpointResolverMiddleware + override val endpointMiddlewareSymbol: Symbol = AWSClientRuntimeTypes.Core.AWSEndpointResolverMiddleware override val unknownServiceErrorSymbol: Symbol = AWSClientRuntimeTypes.Core.UnknownAWSHTTPServiceError } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpProtocolClientGeneratorFactory.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpProtocolClientGeneratorFactory.kt index 1cf3aa03b3d..d33aff5fa91 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpProtocolClientGeneratorFactory.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpProtocolClientGeneratorFactory.kt @@ -24,6 +24,6 @@ class AWSHttpProtocolClientGeneratorFactory : HttpProtocolClientGeneratorFactory operationMiddleware: OperationMiddleware ): HttpProtocolClientGenerator { val config = AWSServiceConfig(writer, ctx) - return HttpProtocolClientGenerator(ctx, writer, config, httpBindingResolver, defaultContentType, httpProtocolCustomizable, operationMiddleware) + return AWSHTTPProtocolClientGenerator(ctx, writer, config, httpBindingResolver, defaultContentType, httpProtocolCustomizable, operationMiddleware) } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpProtocolServiceClient.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpProtocolServiceClient.kt index b21859ac6d2..978d1649aba 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpProtocolServiceClient.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHttpProtocolServiceClient.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.aws.swift.codegen +import software.amazon.smithy.aws.swift.codegen.swiftmodules.AWSClientRuntimeTypes import software.amazon.smithy.aws.swift.codegen.swiftmodules.AWSSDKIdentityTypes import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.traits.HttpBearerAuthTrait @@ -44,8 +45,8 @@ class AWSHttpProtocolServiceClient( } override fun overrideConfigProperties(properties: List): List { - return properties.map { - when (it.name) { + return properties.map { property -> + when (property.name) { "authSchemeResolver" -> { ConfigProperty("authSchemeResolver", SmithyHTTPAuthAPITypes.AuthSchemeResolver, authSchemeResolverDefaultProvider) } @@ -61,7 +62,7 @@ class AWSHttpProtocolServiceClient( true ) } else { - it + property } } "retryStrategyOptions" -> { @@ -100,7 +101,7 @@ class AWSHttpProtocolServiceClient( { it.format("AWSClientConfigDefaultsProvider.httpClientConfiguration()") }, ) } - else -> it + else -> property } } } @@ -119,22 +120,24 @@ class AWSHttpProtocolServiceClient( SwiftTypes.String, ) { writer.openBlock("self.init(", ")") { - renderProperties(properties, true) { - when (it.name) { + properties.forEach { property -> + when (property.name) { "region", "signingRegion" -> { - "region" + writer.write("region,") } "awsCredentialIdentityResolver" -> { - "try AWSClientConfigDefaultsProvider.awsCredentialIdentityResolver()" + writer.write("try AWSClientConfigDefaultsProvider.awsCredentialIdentityResolver(),") } "retryStrategyOptions" -> { - "try AWSClientConfigDefaultsProvider.retryStrategyOptions()" + writer.write("try AWSClientConfigDefaultsProvider.retryStrategyOptions(),") } else -> { - it.default?.render(writer) ?: "nil" + writer.write("\$L,", property.default?.render(writer) ?: "nil") } } } + writer.unwrite(",\n") + writer.write("") } } writer.write("") @@ -142,14 +145,37 @@ class AWSHttpProtocolServiceClient( override fun renderPartitionID() { writer.openBlock("public var partitionID: String? {", "}") { - writer.write("return \"\\(\$L.clientName) - \\(region ?? \"\")\"", serviceConfig.clientName.toUpperCamelCase()) + writer.write( + "return \"\\(\$L.clientName) - \\(region ?? \"\")\"", + serviceConfig.clientName.toUpperCamelCase(), + ) } writer.write("") } private val authSchemeResolverDefaultProvider = DefaultProvider( - { "Default${AuthSchemeResolverGenerator.getSdkId(ctx)}AuthSchemeResolver()" }, + { writer.format("Default\$LAuthSchemeResolver()", AuthSchemeResolverGenerator.getSdkId(ctx)) }, false, false ) + + override fun customizedClientConfigProperty(property: ConfigProperty): ConfigProperty? { + return when (property.name) { + "accountId" -> null // do not expose accountId as a client config property + "accountIdEndpointMode" -> { // expose accountIdEndpointMode as a Swift string-backed enum + ConfigProperty( + "accountIdEndpointMode", + AWSClientRuntimeTypes.Core.AccountIDEndpointMode.toOptional(), + { writer -> + writer.format( + "\$N.accountIDEndpointMode()", + AWSClientRuntimeTypes.Core.AWSClientConfigDefaultsProvider, + ) + }, + true + ) + } + else -> property + } + } } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSMiddlewareExecutionGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSMiddlewareExecutionGenerator.kt new file mode 100644 index 00000000000..6115b3bddc0 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSMiddlewareExecutionGenerator.kt @@ -0,0 +1,29 @@ +package software.amazon.smithy.aws.swift.codegen + +import software.amazon.smithy.swift.codegen.SwiftWriter +import software.amazon.smithy.swift.codegen.config.ConfigProperty +import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable +import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.middleware.MiddlewareExecutionGenerator +import software.amazon.smithy.swift.codegen.middleware.OperationMiddleware + +class AWSMiddlewareExecutionGenerator( + ctx: ProtocolGenerator.GenerationContext, + private val writer: SwiftWriter, + httpBindingResolver: HttpBindingResolver, + httpProtocolCustomizable: HTTPProtocolCustomizable, + operationMiddleware: OperationMiddleware, + operationStackName: String +) : MiddlewareExecutionGenerator( + ctx, writer, httpBindingResolver, httpProtocolCustomizable, operationMiddleware, operationStackName +) { + + override fun renderConfigPropertyToContext(configProperty: ConfigProperty) { + when (configProperty.name) { + "accountIdEndpointMode" -> { // Used for business metrics + writer.write(" .withAccountIDEndpointMode(value: config.accountIdEndpointMode)") + } + } + } +} 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 294d88c00f5..32f541d8a56 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 @@ -21,7 +21,6 @@ import software.amazon.smithy.swift.codegen.utils.toLowerCamelCase const val ENDPOINT_RESOLVER = "endpointResolver" const val AUTH_SCHEME_RESOLVER = "authSchemeResolver" -const val ENDPOINT_PARAMS = "endpointParams" class AWSServiceConfig(writer: SwiftWriter, val ctx: ProtocolGenerator.GenerationContext) : ServiceConfig(writer, ctx.symbolProvider.toSymbol(ctx.service).name, ctx.service.sdkId) { 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 6dd2b8a165d..5c4e83651f4 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 @@ -56,14 +56,6 @@ class PresignerGenerator : SwiftIntegration { renderPresignAPIInServiceClient(writer, symbol.name, op, inputType) } } -// // Import FoundationNetworking statement with preprocessor commands -// if (presignOperations.isNotEmpty()) { -// val symbol = protoCtx.symbolProvider.toSymbol(protoCtx.service) -// protoCtx.delegator.useFileWriter("Sources/${ctx.settings.moduleName}/${symbol.name}.swift") { writer -> -// // In Linux, Foundation.URLRequest is moved to FoundationNetworking. -// writer.addImport(packageName = "FoundationNetworking", importOnlyIfCanImport = true) -// } -// } } private fun renderPresigner( @@ -108,10 +100,7 @@ class PresignerGenerator : SwiftIntegration { operationMiddleware, operationStackName ) - generator.render(serviceShape, op, PRESIGN_REQUEST) { writer, _ -> - writer.write("return nil") - } - + generator.render(serviceShape, op, PRESIGN_REQUEST) writer.write("return try await op.presignRequest(input: input)") } } 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 1a0148fd33e..7962485f48b 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 @@ -137,10 +137,7 @@ class PresignableUrlIntegration(private val presignedOperations: Map - writer.write("return nil") - } - + generator.render(serviceShape, op, PRESIGN_URL) writer.write("return try await op.presignRequest(input: input).endpoint.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 47f7a97edd4..c6b54b3185e 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 @@ -52,7 +52,10 @@ class OperationEndpointResolverMiddleware( // Write code that saves endpoint params to middleware context for use in auth scheme middleware when using rules-based auth scheme resolvers if (AuthSchemeResolverGenerator.usesRulesBasedAuthResolver(ctx)) { - writer.write("context.set(key: \$N(name: \"EndpointParams\"), value: endpointParams)", SmithyTypes.AttributeKey) + writer.write( + "context.set(key: \$N(name: \"EndpointParams\"), value: endpointParamsBlock(context))", + SmithyTypes.AttributeKey + ) } super.renderSpecific(ctx, writer, op, operationStackName, "applyEndpoint") @@ -65,14 +68,13 @@ class OperationEndpointResolverMiddleware( ) { val output = MiddlewareShapeUtils.outputSymbol(ctx.symbolProvider, ctx.model, op) writer.write( - "\$N<\$N, EndpointParams>(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: \$\$0) }, endpointParams: endpointParams)", + "\$N<\$N, EndpointParams>(paramsBlock: endpointParamsBlock, resolverBlock: { [config] in try config.endpointResolver.resolve(params: \$\$0) })", endpointResolverMiddlewareSymbol, output ) } private fun renderEndpointParams(ctx: ProtocolGenerator.GenerationContext, writer: SwiftWriter, op: OperationShape) { - val outputError = MiddlewareShapeUtils.outputErrorSymbol(op) val params = mutableListOf() ctx.service.getTrait()?.ruleSet?.let { node -> val ruleSet = EndpointRuleSet.fromNode(node) @@ -96,7 +98,6 @@ class OperationEndpointResolverMiddleware( operationContextParams[param.name.toString()], clientContextParams[param.name.toString()], writer, - outputError ) value?.let { params.add("$memberName: $it") @@ -104,7 +105,9 @@ class OperationEndpointResolverMiddleware( } } - writer.write("let endpointParams = EndpointParams(${params.joinToString(separator = ", ")})") + writer.openBlock("let endpointParamsBlock = { [config] (context: \$N) in", "}", SmithyTypes.Context) { + writer.write("EndpointParams(\$L)", params.joinToString(", ")) + } } /** @@ -125,7 +128,6 @@ class OperationEndpointResolverMiddleware( operationContextParam: OperationContextParamDefinition?, clientContextParam: ClientContextParamDefinition?, writer: SwiftWriter, - outputError: Symbol ): String? { return when { staticContextParam != null -> { @@ -169,6 +171,12 @@ class OperationEndpointResolverMiddleware( } clientContextParam != null -> { when { + param.name.toString() == "AccountId" -> { + writer.format("context.resolvedAWSAccountID") + } + param.name.toString() == "AccountIdEndpointMode" -> { + "config.accountIdEndpointMode?.rawValue" + } param.default.isPresent -> { "config.${param.name.toString().toLowerCamelCase()} ?? ${param.defaultValueLiteral}" } diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/swiftmodules/AWSClientRuntimeTypes.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/swiftmodules/AWSClientRuntimeTypes.kt index b7ae7a89281..d2ad430c56d 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/swiftmodules/AWSClientRuntimeTypes.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/swiftmodules/AWSClientRuntimeTypes.kt @@ -37,7 +37,8 @@ object AWSClientRuntimeTypes { object Core { val AWSUserAgentMetadata = runtimeSymbol("AWSUserAgentMetadata", SwiftDeclaration.STRUCT) val UserAgentMiddleware = runtimeSymbol("UserAgentMiddleware", SwiftDeclaration.STRUCT) - val EndpointResolverMiddleware = runtimeSymbol("EndpointResolverMiddleware", SwiftDeclaration.STRUCT) + val AWSEndpointResolverMiddleware = runtimeSymbol("AWSEndpointResolverMiddleware", SwiftDeclaration.STRUCT, listOf("AWSEndpointResolverMiddleware")) + val AccountIDEndpointMode = runtimeSymbol("AccountIDEndpointMode", SwiftDeclaration.ENUM) val UnknownAWSHTTPServiceError = runtimeSymbol("UnknownAWSHTTPServiceError", SwiftDeclaration.STRUCT, listOf("UnknownAWSHTTPServiceError")) val AWSServiceError = runtimeSymbol("AWSServiceError", SwiftDeclaration.PROTOCOL) val Sha256TreeHashMiddleware = runtimeSymbol("Sha256TreeHashMiddleware", SwiftDeclaration.STRUCT) 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 99203dfff99..a790f871823 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 @@ -21,7 +21,7 @@ class OperationEndpointResolverMiddlewareTests { val writer = SwiftWriter("smithy.example") val context = setupTests("endpoints.smithy", "smithy.example#ExampleService") val operation = context.ctx.model.operationShapes.toList().first { it.id.name == "GetThing" } - val middleware = OperationEndpointResolverMiddleware(context.ctx, AWSClientRuntimeTypes.Core.EndpointResolverMiddleware) + val middleware = OperationEndpointResolverMiddleware(context.ctx, AWSClientRuntimeTypes.Core.AWSEndpointResolverMiddleware) middleware.render(context.ctx, writer, operation, "operationStack") var contents = writer.toString() val expected = """ @@ -50,8 +50,10 @@ let projection2: [Swift.String]? = objects2?.compactMap { original in let id = original.id return id } -let endpointParams = EndpointParams(boolBar: true, boolBaz: input.fuzz, boolFoo: config.boolFoo, endpoint: config.endpoint, flattenedArray: projection, keysFunctionArray: keys, region: region, stringArrayBar: ["five", "six", "seven"], stringBar: "some value", stringBaz: input.buzz, stringFoo: config.stringFoo, subfield: subfield2, wildcardProjectionArray: projection2) -builder.applyEndpoint(AWSClientRuntime.EndpointResolverMiddleware(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) }, endpointParams: endpointParams)) +let endpointParamsBlock = { [config] (context: Smithy.Context) in + EndpointParams(boolBar: true, boolBaz: input.fuzz, boolFoo: config.boolFoo, endpoint: config.endpoint, flattenedArray: projection, keysFunctionArray: keys, region: region, stringArrayBar: ["five", "six", "seven"], stringBar: "some value", stringBaz: input.buzz, stringFoo: config.stringFoo, subfield: subfield2, wildcardProjectionArray: projection2) +} +builder.applyEndpoint(AWSClientRuntime.AWSEndpointResolverMiddleware(paramsBlock: endpointParamsBlock, resolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) })) """ 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 cf61617853c..6cd1a9daf23 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 @@ -53,8 +53,10 @@ extension GetFooInput { builder.retryStrategy(SmithyRetries.DefaultRetryStrategy(options: config.retryStrategyOptions)) builder.retryErrorInfoProvider(AWSClientRuntime.AWSRetryErrorInfoProvider.errorInfo(for:)) builder.applySigner(ClientRuntime.SignerMiddleware()) - let endpointParams = EndpointParams() - builder.applyEndpoint(AWSClientRuntime.EndpointResolverMiddleware(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) }, endpointParams: endpointParams)) + let endpointParamsBlock = { [config] (context: Smithy.Context) in + EndpointParams() + } + builder.applyEndpoint(AWSClientRuntime.AWSEndpointResolverMiddleware(paramsBlock: endpointParamsBlock, resolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) })) builder.selectAuthScheme(ClientRuntime.AuthSchemeMiddleware()) builder.interceptors.add(AWSClientRuntime.UserAgentMiddleware(serviceID: serviceName, version: ExampleClient.version, config: config)) var metricsAttributes = Smithy.Attributes() @@ -125,8 +127,10 @@ extension PostFooInput { builder.retryStrategy(SmithyRetries.DefaultRetryStrategy(options: config.retryStrategyOptions)) builder.retryErrorInfoProvider(AWSClientRuntime.AWSRetryErrorInfoProvider.errorInfo(for:)) builder.applySigner(ClientRuntime.SignerMiddleware()) - let endpointParams = EndpointParams() - builder.applyEndpoint(AWSClientRuntime.EndpointResolverMiddleware(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) }, endpointParams: endpointParams)) + let endpointParamsBlock = { [config] (context: Smithy.Context) in + EndpointParams() + } + builder.applyEndpoint(AWSClientRuntime.AWSEndpointResolverMiddleware(paramsBlock: endpointParamsBlock, resolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) })) builder.selectAuthScheme(ClientRuntime.AuthSchemeMiddleware()) builder.interceptors.add(AWSClientRuntime.UserAgentMiddleware(serviceID: serviceName, version: ExampleClient.version, config: config)) var metricsAttributes = Smithy.Attributes() @@ -197,8 +201,10 @@ extension PutFooInput { builder.retryStrategy(SmithyRetries.DefaultRetryStrategy(options: config.retryStrategyOptions)) builder.retryErrorInfoProvider(AWSClientRuntime.AWSRetryErrorInfoProvider.errorInfo(for:)) builder.applySigner(ClientRuntime.SignerMiddleware()) - let endpointParams = EndpointParams() - builder.applyEndpoint(AWSClientRuntime.EndpointResolverMiddleware(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) }, endpointParams: endpointParams)) + let endpointParamsBlock = { [config] (context: Smithy.Context) in + EndpointParams() + } + builder.applyEndpoint(AWSClientRuntime.AWSEndpointResolverMiddleware(paramsBlock: endpointParamsBlock, resolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) })) builder.selectAuthScheme(ClientRuntime.AuthSchemeMiddleware()) builder.interceptors.add(AWSClientRuntime.UserAgentMiddleware(serviceID: serviceName, version: ExampleClient.version, config: config)) var metricsAttributes = Smithy.Attributes() @@ -269,9 +275,11 @@ extension PutObjectInput { builder.retryStrategy(SmithyRetries.DefaultRetryStrategy(options: config.retryStrategyOptions)) builder.retryErrorInfoProvider(AWSClientRuntime.AWSRetryErrorInfoProvider.errorInfo(for:)) builder.applySigner(ClientRuntime.SignerMiddleware()) - let endpointParams = EndpointParams() - context.set(key: Smithy.AttributeKey(name: "EndpointParams"), value: endpointParams) - builder.applyEndpoint(AWSClientRuntime.EndpointResolverMiddleware(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) }, endpointParams: endpointParams)) + let endpointParamsBlock = { [config] (context: Smithy.Context) in + EndpointParams() + } + context.set(key: Smithy.AttributeKey(name: "EndpointParams"), value: endpointParamsBlock(context)) + builder.applyEndpoint(AWSClientRuntime.AWSEndpointResolverMiddleware(paramsBlock: endpointParamsBlock, resolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) })) builder.selectAuthScheme(ClientRuntime.AuthSchemeMiddleware()) builder.interceptors.add(AWSClientRuntime.UserAgentMiddleware(serviceID: serviceName, version: S3Client.version, config: config)) var metricsAttributes = Smithy.Attributes() 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 9cbab46b7a7..914e085df23 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 @@ -36,8 +36,10 @@ class AWSQueryOperationStackTest { builder.retryStrategy(SmithyRetries.DefaultRetryStrategy(options: config.retryStrategyOptions)) builder.retryErrorInfoProvider(AWSClientRuntime.AWSRetryErrorInfoProvider.errorInfo(for:)) builder.applySigner(ClientRuntime.SignerMiddleware()) - let endpointParams = EndpointParams() - builder.applyEndpoint(AWSClientRuntime.EndpointResolverMiddleware(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) }, endpointParams: endpointParams)) + let endpointParamsBlock = { [config] (context: Smithy.Context) in + EndpointParams() + } + builder.applyEndpoint(AWSClientRuntime.AWSEndpointResolverMiddleware(paramsBlock: endpointParamsBlock, resolverBlock: { [config] in try config.endpointResolver.resolve(params: ${'$'}0) })) builder.serialize(ClientRuntime.BodyMiddleware(rootNodeInfo: "", inputWritingClosure: NoInputAndOutputInput.write(value:to:))) builder.interceptors.add(ClientRuntime.ContentTypeMiddleware(contentType: "application/x-www-form-urlencoded")) builder.selectAuthScheme(ClientRuntime.AuthSchemeMiddleware()) diff --git a/scripts/integration-test-sdk.properties b/scripts/integration-test-sdk.properties index 166be55dff9..b7b124dcc5e 100644 --- a/scripts/integration-test-sdk.properties +++ b/scripts/integration-test-sdk.properties @@ -1,2 +1,2 @@ # Only include services needed for running integration tests -onlyIncludeModels=kinesis,s3,sso-admin,transcribe-streaming,sqs,mediaconvert,sts,cognito-identity,iam,ec2,ecs,cloudwatch-logs,s3-control,eventbridge,cloudfront,cloudfront-keyvaluestore,route-53,glacier +onlyIncludeModels=kinesis,s3,sso-admin,transcribe-streaming,sqs,mediaconvert,sts,cognito-identity,iam,ec2,ecs,cloudwatch-logs,s3-control,eventbridge,cloudfront,cloudfront-keyvaluestore,route-53,glacier,dynamodb