Skip to content

Commit

Permalink
feat: add interceptor client config
Browse files Browse the repository at this point in the history
Allows configuration of interceptors on a client level by adding interceptor
providers to client config, allowing Plugins to add interceptors.

The primary addition is `InterceptorProvider`, an interface that creates
generic interceptors which can operate on any transport - http or otherwise.
When an operation is executed, interceptor providers are called to create
new instances of service-level interceptors. Creating new instances also
means we don't need to synchronize on the shared interceptors. Transport
specific config, have their own methods for adding interceptor providers, so
you can add `HttpInterceptorProvider`s to http config. Operations know which
transport they operate on, and can choose which transport-specific interceptor
providers to use.

If/when we have operation-level configuration, it might make more sense to
allow plugins to configure more generic 'operation customizations' or
something, rather than just the interceptors. Operations would then call
the customizations before executing.

A few other minor changes were made to the client libraries:
- Removed HasAttributes protocol and corresponding AttributesType from
interceptor interfaces, as we now just use `Context`.
- Changed InterceptorContext `getResult` to `getOutput`. We still store a
`Result<OutputType, Error>` in DefaultInterceptorContext, but `getOutput`
now throws that error if it is present, otherwise just returns `OutputType`
- Added actual builder methods to RequestMessageBuilder, which we would need
eventually, so I could use them in testing
- Made SdkHttpRequestBuilder final

Codegen was also updated to generate the new config methods, and to call
interceptor providers in operations to add configured interceptors.
  • Loading branch information
milesziemer committed Jun 12, 2024
1 parent 27a3724 commit bdbfa0b
Show file tree
Hide file tree
Showing 49 changed files with 617 additions and 355 deletions.
5 changes: 5 additions & 0 deletions Sources/ClientRuntime/Config/DefaultClientConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@ public protocol DefaultClientConfiguration: ClientConfiguration {
/// If none is provided, only a default logger provider will be used.
var telemetryProvider: TelemetryProvider { get set }

/// Add an `InterceptorProvider` that will be used to provide interceptors for all operations.
///
/// - Parameter provider: The `InterceptorProvider` to add.
func addInterceptorProvider(_ provider: InterceptorProvider)

/// TODO(plugins): Add Checksum, etc.
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ public protocol DefaultHttpClientConfiguration: ClientConfiguration {
///
/// Defaults to a auth scheme resolver generated based on Smithy service model.
var authSchemeResolver: AuthSchemeResolver { get set }

/// Add an `HttpInterceptorProvider` that will be used to provide interceptors for all HTTP operations.
///
/// - Parameter provider: The `HttpInterceptorProvider` to add.
func addInterceptorProvider(_ provider: HttpInterceptorProvider)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// SPDX-License-Identifier: Apache-2.0
//

import protocol Smithy.HasAttributes
import class Smithy.Context

public struct IdempotencyTokenMiddleware<OperationStackInput, OperationStackOutput>: Middleware {
Expand Down Expand Up @@ -42,7 +41,7 @@ extension IdempotencyTokenMiddleware: HttpInterceptor {
public typealias InputType = OperationStackInput
public typealias OutputType = OperationStackOutput

public func modifyBeforeSerialization(context: some MutableInput<InputType, AttributesType>) async throws {
public func modifyBeforeSerialization(context: some MutableInput<InputType>) async throws {
let withToken = addToken(input: context.getInput(), attributes: context.getAttributes())
context.updateInput(updated: withToken)
}
Expand Down
10 changes: 4 additions & 6 deletions Sources/ClientRuntime/Interceptor/AnyInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
// SPDX-License-Identifier: Apache-2.0
//

import class Smithy.Context
import protocol Smithy.RequestMessage
import protocol Smithy.ResponseMessage
import protocol Smithy.HasAttributes

/// Type-erased, concrete interceptor.
///
Expand All @@ -22,11 +22,10 @@ internal struct AnyInterceptor<
InputType,
OutputType,
RequestType: RequestMessage,
ResponseType: ResponseMessage,
AttributesType: HasAttributes
ResponseType: ResponseMessage
> {
internal typealias InterceptorContextType = DefaultInterceptorContext<
InputType, OutputType, RequestType, ResponseType, AttributesType
InputType, OutputType, RequestType, ResponseType
>
internal typealias InterceptorFn = (InterceptorContextType) async throws -> Void

Expand Down Expand Up @@ -55,8 +54,7 @@ internal struct AnyInterceptor<
I.InputType == InputType,
I.OutputType == OutputType,
I.RequestType == RequestType,
I.ResponseType == ResponseType,
I.AttributesType == AttributesType {
I.ResponseType == ResponseType {
self.readBeforeExecution = interceptor.readBeforeExecution(context:)
self.modifyBeforeSerialization = interceptor.modifyBeforeSerialization(context:)
self.readBeforeSerialization = interceptor.readBeforeSerialization(context:)
Expand Down
28 changes: 18 additions & 10 deletions Sources/ClientRuntime/Interceptor/DefaultInterceptorContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
// SPDX-License-Identifier: Apache-2.0
//

import class Smithy.Context
import protocol Smithy.RequestMessage
import protocol Smithy.ResponseMessage
import protocol Smithy.HasAttributes

/// Default implementation for all interceptor context types.
///
Expand All @@ -17,16 +17,15 @@ public class DefaultInterceptorContext<
InputType,
OutputType,
RequestType: RequestMessage,
ResponseType: ResponseMessage,
AttributesType: HasAttributes
ResponseType: ResponseMessage
>: InterceptorContext {
private var attributes: AttributesType
private var attributes: Context
private var input: InputType
private var request: RequestType?
private var response: ResponseType?
private var result: Result<OutputType, Error>?

public init(input: InputType, attributes: AttributesType) {
public init(input: InputType, attributes: Context) {
self.input = input
self.attributes = attributes
}
Expand All @@ -35,9 +34,13 @@ public class DefaultInterceptorContext<
self.input
}

public func getAttributes() -> AttributesType {
public func getAttributes() -> Context {
return self.attributes
}

Check warning on line 40 in Sources/ClientRuntime/Interceptor/DefaultInterceptorContext.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Lines should not have trailing whitespace (trailing_whitespace)
internal func setResult(result: Result<OutputType, Error>) {
self.result = result
}
}

extension DefaultInterceptorContext: BeforeSerialization {}
Expand Down Expand Up @@ -73,8 +76,13 @@ extension DefaultInterceptorContext: MutableResponse {
}

extension DefaultInterceptorContext: AfterDeserialization {
public func getResult() -> Result<OutputType, Error> {
self.result!
public func getOutput() throws -> OutputType {
switch self.result! {
case .success(let output):
return output
case .failure(let error):
throw error
}
}
}

Expand All @@ -85,8 +93,8 @@ extension DefaultInterceptorContext: AfterAttempt {
}

extension DefaultInterceptorContext: MutableOutputAfterAttempt {
public func updateResult(updated: Result<OutputType, Error>) {
self.result = updated
public func updateOutput(updated: OutputType) {
self.result = .success(updated)
}
}

Expand Down
20 changes: 0 additions & 20 deletions Sources/ClientRuntime/Interceptor/HasAttributes+Logger.swift

This file was deleted.

5 changes: 1 addition & 4 deletions Sources/ClientRuntime/Interceptor/HttpInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,4 @@ import class SmithyHTTPAPI.SdkHttpRequest
import class SmithyHTTPAPI.HttpResponse

public protocol HttpInterceptor<InputType, OutputType>: Interceptor
where
RequestType == SdkHttpRequest,
ResponseType == HttpResponse,
AttributesType == Smithy.Context {}
where RequestType == SdkHttpRequest, ResponseType == HttpResponse {}
17 changes: 17 additions & 0 deletions Sources/ClientRuntime/Interceptor/HttpInterceptorProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

/// Provides implementations of `HttpInterceptor`.
///
/// For the generic counterpart, see `InterceptorProvider`.
public protocol HttpInterceptorProvider {

/// Creates an instance of an `HttpInterceptor` implementation.
///
/// - Returns: The `HttpInterceptor` implementation.
func create<InputType, OutputType>() -> any HttpInterceptor<InputType, OutputType>
}
Loading

0 comments on commit bdbfa0b

Please sign in to comment.