Skip to content

Commit

Permalink
feat: add runtime plugins (#650)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewFossAWS authored Feb 19, 2024
1 parent 64e66f3 commit d198bba
Show file tree
Hide file tree
Showing 25 changed files with 460 additions and 82 deletions.
10 changes: 10 additions & 0 deletions Sources/ClientRuntime/Client/Client.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
public protocol Client {
associatedtype Config: ClientConfiguration
init(config: Config)
}
32 changes: 32 additions & 0 deletions Sources/ClientRuntime/Client/ClientBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
public class ClientBuilder<ClientType: Client> {

private var plugins: [Plugin]

public init(defaultPlugins: [Plugin] = []) {
self.plugins = defaultPlugins
}

public func withPlugin(_ plugin: any Plugin) -> ClientBuilder<ClientType> {
self.plugins.append(plugin)
return self
}

public func build() async throws -> ClientType {
let configuration = try await resolve(plugins: self.plugins)
return ClientType(config: configuration)
}

func resolve(plugins: [any Plugin]) async throws -> ClientType.Config {
let clientConfiguration = try await ClientType.Config()
for plugin in plugins {
try await plugin.configureClient(clientConfiguration: clientConfiguration)
}
return clientConfiguration
}
}
10 changes: 10 additions & 0 deletions Sources/ClientRuntime/Config/ClientConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public protocol ClientConfiguration {
init() async throws
}
33 changes: 33 additions & 0 deletions Sources/ClientRuntime/Config/DefaultClientConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public protocol DefaultClientConfiguration: ClientConfiguration {
/// The logger to be used for client activity.
///
/// If none is provided, the SDK's logger will be used.
var logger: LogAgent { get set }

/// The configuration for retry of failed network requests.
///
/// Default options are provided if none are set.
var retryStrategyOptions: RetryStrategyOptions { get set }

/// The log mode to use for client logging.
///
/// If none is provided, `.request` will be used.
var clientLogMode: ClientLogMode { get set }

/// The network endpoint to use.
///
/// If none is provided, the service will select its own endpoint to use.
var endpoint: String? { get set }

/// A token generator to ensure idempotency of requests.
var idempotencyTokenGenerator: IdempotencyTokenGenerator { get set }

/// TODO(plugins): Add Checksum, Traceprobes, etc.
}
20 changes: 20 additions & 0 deletions Sources/ClientRuntime/Config/DefaultHttpClientConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public protocol DefaultHttpClientConfiguration: ClientConfiguration {

/// The HTTP client to be used for the target platform, configured with the supplied configuration.
///
/// By default, Swift SDK will set this to `CRTClientEngine` client on Mac & Linux platforms,
/// or `URLSessionHttpClient` on non-Mac Apple platforms.
var httpClientEngine: HTTPClient { get set }

/// Configuration for the HTTP client.
var httpClientConfiguration: HttpClientConfiguration { get }

/// TODO: Add auth scheme
}
18 changes: 18 additions & 0 deletions Sources/ClientRuntime/Plugins/DefaultClientPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public class DefaultClientPlugin: Plugin {
public init() {}
public func configureClient(clientConfiguration: ClientConfiguration) {
if var config = clientConfiguration as? DefaultClientConfiguration {
// Populate default values for configuration here if they are missing
config.retryStrategyOptions =
DefaultSDKRuntimeConfiguration<DefaultRetryStrategy, DefaultRetryErrorInfoProvider>
.defaultRetryStrategyOptions
}
}
}
10 changes: 10 additions & 0 deletions Sources/ClientRuntime/Plugins/Plugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

public protocol Plugin {
func configureClient(clientConfiguration: ClientConfiguration) async throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import software.amazon.smithy.swift.codegen.model.buildSymbol
*/
object ClientRuntimeTypes {
object Http {
val HttpClientEngine = runtimeSymbol("HttpClientEngine")
val HttpClient = runtimeSymbol("HTTPClient")
val HttpClientConfiguration = runtimeSymbol("HttpClientConfiguration")
val Headers = runtimeSymbol("Headers")
val HttpStatusCode = runtimeSymbol("HttpStatusCode")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package software.amazon.smithy.swift.codegen

import software.amazon.smithy.swift.codegen.config.ClientConfiguration
import software.amazon.smithy.swift.codegen.config.DefaultClientConfiguration
import software.amazon.smithy.swift.codegen.config.DefaultHttpClientConfiguration
import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
import software.amazon.smithy.swift.codegen.integration.SwiftIntegration

class DefaultClientConfigurationIntegration : SwiftIntegration {
override fun clientConfigurations(ctx: ProtocolGenerator.GenerationContext): List<ClientConfiguration> {
return listOf(DefaultClientConfiguration(), DefaultHttpClientConfiguration())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package software.amazon.smithy.swift.codegen

import software.amazon.smithy.codegen.core.SymbolDependencyContainer

interface Dependency : SymbolDependencyContainer {
val target: String
var packageName: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@
package software.amazon.smithy.swift.codegen

import software.amazon.smithy.codegen.core.SymbolDependency
import software.amazon.smithy.codegen.core.SymbolDependencyContainer
import software.amazon.smithy.swift.codegen.resources.Resources

enum class SwiftDependency(
val target: String,
override val target: String,
private val branch: String? = null,
val version: String,
private val version: String,
private val url: String,
private val localPath: String,
var packageName: String
) : SymbolDependencyContainer {
override var packageName: String
) : Dependency {
BIG("ComplexModule", null, "0.0.5", "https://github.com/apple/swift-numerics", "", "swift-numerics"),
SWIFT_LOG("Logging", null, "", "", "", ""),
CLIENT_RUNTIME(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package software.amazon.smithy.swift.codegen.config

import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.swift.codegen.Dependency
import software.amazon.smithy.swift.codegen.model.buildSymbol

/**
* Specifies the behaviour of the service configuration
*/
interface ClientConfiguration {
/**
* The protocol name of the client configuration
*/
val swiftProtocolName: Symbol?

val properties: Set<ConfigProperty>

companion object {
fun runtimeSymbol(
name: String,
dependency: Dependency?,
): Symbol = buildSymbol {
this.name = name
dependency?.also {
this.namespace = it.target
dependency(dependency)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package software.amazon.smithy.swift.codegen.config

import software.amazon.smithy.codegen.core.Symbol
data class ConfigProperty(
val name: String,
val type: Symbol,
val default: DefaultProvider? = null
) {
constructor(
name: String,
type: Symbol,
default: String,
isThrowable: Boolean = false,
isAsync: Boolean = false
) : this(name, type, DefaultProvider(default, isThrowable, isAsync))

val isOptional: Boolean = type.name.endsWith('?')

fun toOptionalType(): String {
return if (isOptional) type.name else "${type.name}?"
}

init {
if (!this.isOptional && default == null)
throw RuntimeException("Non-optional client config property must have a default value")
}
}
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.
*/

package software.amazon.smithy.swift.codegen.config

import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.swift.codegen.ClientRuntimeTypes
import software.amazon.smithy.swift.codegen.SwiftDependency
import software.amazon.smithy.swift.codegen.SwiftTypes
import software.amazon.smithy.swift.codegen.config.ClientConfiguration.Companion.runtimeSymbol
import software.amazon.smithy.swift.codegen.model.toNullable

class DefaultClientConfiguration : ClientConfiguration {
override val swiftProtocolName: Symbol
get() = runtimeSymbol("DefaultClientConfiguration", SwiftDependency.CLIENT_RUNTIME)

override val properties: Set<ConfigProperty>
get() = setOf(
ConfigProperty("logger", ClientRuntimeTypes.Core.Logger, "AWSClientConfigDefaultsProvider.logger(clientName)"),
ConfigProperty("retryStrategyOptions", ClientRuntimeTypes.Core.RetryStrategyOptions, "AWSClientConfigDefaultsProvider.retryStrategyOptions()", true),
ConfigProperty("clientLogMode", ClientRuntimeTypes.Core.ClientLogMode, "AWSClientConfigDefaultsProvider.clientLogMode"),
ConfigProperty("endpoint", SwiftTypes.String.toNullable()),
ConfigProperty("idempotencyTokenGenerator", ClientRuntimeTypes.Core.IdempotencyTokenGenerator, "AWSClientConfigDefaultsProvider.idempotencyTokenGenerator"),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package software.amazon.smithy.swift.codegen.config

import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.swift.codegen.ClientRuntimeTypes
import software.amazon.smithy.swift.codegen.SwiftDependency
import software.amazon.smithy.swift.codegen.config.ClientConfiguration.Companion.runtimeSymbol

class DefaultHttpClientConfiguration : ClientConfiguration {
override val swiftProtocolName: Symbol
get() = runtimeSymbol("DefaultHttpClientConfiguration", SwiftDependency.CLIENT_RUNTIME)

override val properties: Set<ConfigProperty>
get() = setOf(
ConfigProperty("httpClientEngine", ClientRuntimeTypes.Http.HttpClient, "AWSClientConfigDefaultsProvider.httpClientEngine"),
ConfigProperty("httpClientConfiguration", ClientRuntimeTypes.Http.HttpClientConfiguration, "AWSClientConfigDefaultsProvider.httpClientConfiguration"),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package software.amazon.smithy.swift.codegen.config

data class DefaultProvider(
val value: String,
val isThrowable: Boolean,
val isAsync: Boolean
) {
/**
* Returns a default value for client configuration
*
* For example:
*
* // isAsync = true, isThrowable = true, paramNilCheck = null, default = DefaultProvider.region()
* try await DefaultProvider.region(),
*
* // isAsync = false, isThrowable = true, paramNilCheck = retryMode, default = DefaultProvider.region()
* try retryMode ?? DefaultProvider.region(),
*
* // isAsync = false, isThrowable = true, paramNilCheck = retryMode, default = DefaultProvider.logger()
* DefaultProvider.logger()
*
* @param paramNilCheck parameter to nil check
* @return default value.
*/
fun render(paramNilCheck: String? = null): String {
var res = value
if (paramNilCheck != null)
res = "$paramNilCheck ?? $res"
if (isAsync)
res = "await $res"
if (isThrowable)
res = "try $res"
return res
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ abstract class HttpRequestEncoder(private val requestEncoder: Symbol, private va
}

override fun renderInitialization(writer: SwiftWriter, nameOfConfigObject: String) {
writer.write("self.encoder = \$L.encoder ?? \$L", nameOfConfigObject, name)
writer.write("self.encoder = \$L", name)
}
}

Expand All @@ -102,7 +102,7 @@ abstract class HttpResponseDecoder(private val requestDecoder: Symbol, private v
}

override fun renderInitialization(writer: SwiftWriter, nameOfConfigObject: String) {
writer.write("self.decoder = \$L.decoder ?? \$L", nameOfConfigObject, name)
writer.write("self.decoder = \$L", name)
}
}

Expand Down
Loading

0 comments on commit d198bba

Please sign in to comment.