Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Account ID-based endpoints #1841

Merged
merged 38 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c8288c0
Update codegen
jbelkins Dec 3, 2024
09f8729
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 4, 2024
3ee4347
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 5, 2024
c1315df
feat: Account ID-based endpoints
jbelkins Dec 6, 2024
6aa9a90
Fix tests
jbelkins Dec 6, 2024
cada066
Wire up account ID
jbelkins Dec 6, 2024
f85862e
Add account ID endpoint mode resolution
jbelkins Dec 9, 2024
93e0634
fix tests
jbelkins Dec 9, 2024
adea19b
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 9, 2024
34769a4
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 10, 2024
c8aec20
Integrate CRT support for account ID, add tests
jbelkins Dec 12, 2024
b3c0987
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 12, 2024
b632492
Generate dynamodb for integration tests
jbelkins Dec 12, 2024
c1543cd
Set region in endpoint mode test
jbelkins Dec 12, 2024
8c5c3e5
Add business metrics
jbelkins Dec 13, 2024
82da91b
Several lint and config fixes
jbelkins Dec 17, 2024
dd08cd2
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 17, 2024
095dd5b
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 17, 2024
d1908b3
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 19, 2024
6e4ef4d
Merge branch 'jbe/account_id_config' of github.com:awslabs/aws-sdk-sw…
jbelkins Dec 19, 2024
1952b95
Merge branch 'main' into jbe/account_id_config
jbelkins Dec 27, 2024
3f2fb2a
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 6, 2025
73a21dc
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 7, 2025
49202bc
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 7, 2025
869707f
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 9, 2025
4b734e9
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 10, 2025
31ffa70
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 14, 2025
51db47a
Fix merge & tests
jbelkins Jan 14, 2025
f4f96a6
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 15, 2025
58c0582
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 15, 2025
6eeb569
Changes from code review
jbelkins Jan 15, 2025
2b6c726
Fix codegen tests
jbelkins Jan 15, 2025
388ba40
Fix tests
jbelkins Jan 16, 2025
b98e583
Revert next version
jbelkins Jan 16, 2025
6118c4a
Add accountIDEndpointMode to context only when defined
jbelkins Jan 16, 2025
a9f25a3
fix ktlint
jbelkins Jan 16, 2025
f06420a
Merge branch 'main' into jbe/account_id_config
jbelkins Jan 16, 2025
6367b7e
Bumping next version to 1.2.0
jbelkins Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions IntegrationTests/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ let package = Package(
private var integrationTestTargets: [Target] {
let integrationTests = [
"AWSCloudFrontKeyValueStore",
"AWSDynamoDB",
"AWSEC2",
"AWSECS",
"AWSEventBridge",
Expand Down Expand Up @@ -112,3 +113,4 @@ private func integrationTestTarget(_ name: String) -> Target {
resources: [.process("Resources")]
)
}

Original file line number Diff line number Diff line change
@@ -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 {
jbelkins marked this conversation as resolved.
Show resolved Hide resolved
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)
}
}
Empty file.
55 changes: 31 additions & 24 deletions IntegrationTests/XCTestPlans/AWSIntegrationTestsOnCI.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,43 @@
{
"target" : {
"containerPath" : "container:",
jbelkins marked this conversation as resolved.
Show resolved Hide resolved
"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"
}
},
{
Expand All @@ -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"
}
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,19 @@ public class AWSClientConfigDefaultsProvider {
fileBasedConfig: fileBasedConfig
)
}

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
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ import struct Smithy.SwiftLogger
@_spi(FileBasedConfig) import AWSSDKCommon

public enum AWSEndpointConfig {

jbelkins marked this conversation as resolved.
Show resolved Hide resolved
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
}

static func configuredEndpoint(
sdkID: String,
ignoreConfiguredEndpointURLs: Bool?,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

jbelkins marked this conversation as resolved.
Show resolved Hide resolved
/// 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
}
Original file line number Diff line number Diff line change
@@ -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

jbelkins marked this conversation as resolved.
Show resolved Hide resolved
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<AccountIDEndpointMode>(name: "AccountIDEndpointMode")
Loading
Loading