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: add default client configurations #1312

Merged
merged 2 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import XCTest
import AWSKinesis
import ClientRuntime
import AWSClientRuntime

class KinesisTests: XCTestCase {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AWSMediaConvertTests: XCTestCase {
}

// Create a client, configured to use the endpoint you just retrieved.
let config = try MediaConvertClient.MediaConvertClientConfiguration(region: region, endpoint: endpoint)
let config = try await MediaConvertClient.MediaConvertClientConfiguration(region: region, signingRegion: region, endpoint: endpoint)
let client = MediaConvertClient(config: config)

let name = "Android TV Template"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class S3ErrorTests: S3XCTestCase {
do {
let credentials = Credentials(accessKey: "AKIDEXAMPLE", secret: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY")
let credentialsProvider = try StaticCredentialsProvider(credentials)
let config = try S3Client.S3ClientConfiguration(region: region, credentialsProvider: credentialsProvider)
let config = try await S3Client.S3ClientConfiguration(credentialsProvider: credentialsProvider, region: region)
let input = GetObjectInput(bucket: bucketName, key: UUID().uuidString)
_ = try await S3Client(config: config).getObject(input: input)
XCTFail("Request should not have succeeded")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import XCTest
import AWSSQS
import ClientRuntime
import AWSClientRuntime

/// Tests AWS SQS queue creation and deletion.
class SQSTests: XCTestCase {
Expand All @@ -17,7 +18,7 @@ class SQSTests: XCTestCase {
private var queueUrl: String?

override func setUp() async throws {
client = try await SQSClient()
self.client = try SQSClient(region: "us-west-1")
queueName = "integration-test-queue-\(UUID().uuidString)"
}

Expand Down
103 changes: 103 additions & 0 deletions Sources/Core/AWSClientRuntime/AWSClientConfigDefaultsProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import ClientRuntime

typealias RuntimeConfigType = DefaultSDKRuntimeConfiguration<DefaultRetryStrategy, DefaultRetryErrorInfoProvider>

/// Provides default configuration properties for AWS services.
public class AWSClientConfigDefaultsProvider {

public static let httpClientEngine: HTTPClient = RuntimeConfigType.makeClient(
httpClientConfiguration: RuntimeConfigType.defaultHttpClientConfiguration)

public static let httpClientConfiguration: HttpClientConfiguration
= RuntimeConfigType.defaultHttpClientConfiguration

public static let idempotencyTokenGenerator: IdempotencyTokenGenerator
= RuntimeConfigType.defaultIdempotencyTokenGenerator

public static func logger(_ clientName: String) -> LogAgent {
return RuntimeConfigType.defaultLogger(clientName: clientName)
}

public static let clientLogMode: ClientLogMode = RuntimeConfigType.defaultClientLogMode

public static func credentialsProvider(
_ credentialsProvider: AWSClientRuntime.CredentialsProviding? = nil) throws -> CredentialsProviding {
let resolvedCredentialsProvider: AWSClientRuntime.CredentialsProviding
let fileBasedConfig = try CRTFileBasedConfiguration.make()
if let credentialsProvider = credentialsProvider {
resolvedCredentialsProvider = credentialsProvider
} else {
resolvedCredentialsProvider = try DefaultChainCredentialsProvider(fileBasedConfig: fileBasedConfig)
}
return resolvedCredentialsProvider
}

public static func region(_ region: String? = nil) async throws -> String? {
let resolvedRegion: String?
let fileBasedConfig = try await CRTFileBasedConfiguration.makeAsync()
if let region = region {
resolvedRegion = region
} else {
let regionResolver = try DefaultRegionResolver { _, _ in fileBasedConfig }
resolvedRegion = await regionResolver.getRegion()
}

return resolvedRegion
}

public static func appID(_ appID: String? = nil) throws -> String? {
let fileBasedConfig = try CRTFileBasedConfiguration.make()
let resolvedAppID: String?
if let appID = appID {
resolvedAppID = appID
} else {
resolvedAppID = AppIDConfig.appID(
configValue: nil,
profileName: nil,
fileBasedConfig: fileBasedConfig
)
}
return resolvedAppID
}

public static func retryMode(_ retryMode: AWSRetryMode? = nil) throws -> AWSRetryMode {
let fileBasedConfig = try CRTFileBasedConfiguration.make()
let resolvedRetryMode: AWSRetryMode?
if let retryMode = retryMode {
resolvedRetryMode = retryMode
} else {
resolvedRetryMode = AWSRetryConfig.retryMode(
configValue: nil,
profileName: nil,
fileBasedConfig: fileBasedConfig
)
}
return resolvedRetryMode!
}

public static func retryStrategyOptions() throws -> RetryStrategyOptions {
let fileBasedConfig = try CRTFileBasedConfiguration.make()
let resolvedMaxAttempts = AWSRetryConfig.maxAttempts(
configValue: nil,
profileName: nil,
fileBasedConfig: fileBasedConfig
)

let resolvedRateLimitingMode: RetryStrategyOptions.RateLimitingMode

switch try self.retryMode(nil) {
case .legacy, .standard:
resolvedRateLimitingMode = .standard
case .adaptive:
resolvedRateLimitingMode = .adaptive
}

return RetryStrategyOptions(maxRetriesBase: resolvedMaxAttempts - 1, rateLimitingMode: resolvedRateLimitingMode)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public protocol AWSDefaultClientConfiguration {
/// 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.
var credentialsProvider: CredentialsProviding { get set }

/// Specifies whether FIPS endpoints should be used.
var useFIPS: Bool? { get set }

/// Specifies whether dual-stack endpoints should be used.
var useDualStack: Bool? { get set }

/// An identifying string for the application being used with this SDK.
///
/// This value is set after resolving app ID from the standard progression of potential sources.
///
/// The application ID is submitted as part of the `user-agent` and allows for better tracking and troubleshooting.
/// The application ID may also be retrieved from the environment variable `AWS_SDK_UA_APP_ID` or from the
/// configuration file field `sdk_ua_app_id` if it is not set here.
var appID: String? { get set }

/// The AWS retry mode to be used.
///
/// This value is set after resolving retry mode from the standard progression of potential sources.
///
/// May be one of `legacy`, `standard`, or `adaptive`. `standard` and `adaptive` retry strategies are as defined in
/// Smithy Reference Architecture. For the Swift SDK, `legacy` is the same behavior as `standard`.
var awsRetryMode: AWSRetryMode { get set }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public protocol AWSRegionClientConfiguration {

/// 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.
var region: String? { get set }

/// The signing region to be used for signing AWS requests.
///
/// If none is specified, it is supplied by the SDK.
var signingRegion: String? { get set }
}
24 changes: 24 additions & 0 deletions Sources/Core/AWSClientRuntime/Plugins/DefaultAWSClientPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import ClientRuntime

public class DefaultAWSClientPlugin: Plugin {
private var clientName: String

public init(clientName: String) {
self.clientName = clientName
}

public func configureClient(clientConfiguration: ClientConfiguration) throws {
if var config = clientConfiguration as? DefaultClientConfiguration
& AWSDefaultClientConfiguration
& AWSRegionClientConfiguration {
config.retryStrategyOptions = try AWSClientConfigDefaultsProvider.retryStrategyOptions()
}
}
}
23 changes: 23 additions & 0 deletions Sources/Core/AWSClientRuntime/Plugins/RegionPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import ClientRuntime

public class RegionPlugin: Plugin {
private var region: String

public init(_ region: String) {
self.region = region
}

public func configureClient(clientConfiguration: ClientConfiguration) {
if var config = clientConfiguration as? AWSRegionClientConfiguration {
config.region = self.region
config.signingRegion = self.region
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public struct EnvironmentRegionProvider: RegionProvider {
self.env = env
}

public func getRegion() async throws -> String? {
public func getRegion() throws -> String? {
return env.environmentVariable(key: AWS_ENVIRON_REGION)
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Core/AWSClientRuntime/Retry/AWSRetryConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import struct ClientRuntime.RetryStrategyOptions

enum AWSRetryConfig {
public enum AWSRetryConfig {

/// Determines the retry mode to be used from the given config. If none can be determined, `legacy` will be used as a default.
/// - Parameters:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ public struct AWSUserAgentMetadata {
self.frameworkMetadata = frameworkMetadata
}

public static func fromConfig<ServiceSpecificConfiguration>(
public static func fromConfig(
serviceID: String,
version: String,
config: AWSClientConfiguration<ServiceSpecificConfiguration>
config: DefaultClientConfiguration & AWSDefaultClientConfiguration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This narrows down service specific configurations to default configurations (which I think is good).

But is there a use case where we might need service specific configuration?

Copy link
Contributor Author

@AndrewFossAWS AndrewFossAWS Feb 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored the fromConfig function because 2) AWSClientConfiguration is going to be deprecated and 2) the function only uses variables specified by the two configuration protocols. (it doesn't use any service specific stuff)

) -> AWSUserAgentMetadata {
let apiMetadata = APIMetadata(serviceID: serviceID, version: version)
let sdkMetadata = SDKMetadata(version: apiMetadata.version)
Expand Down
Loading
Loading