Skip to content

Conversation

@aajtodd
Copy link
Contributor

@aajtodd aajtodd commented Jan 13, 2023

Issue #

N/A

Description of changes

This PR refactors client configuration to provide a more structured approach to the way it is generated.

  • Proposes a new convention doc for client configuration that describes how new configurations should be added and in general how they are used by the SDK. This isn't a normal design doc but a convention/guidelines doc.
  • Define new builder interfaces for specific domain configurations (e.g. HttpClientConfig.Builder, TracingClientConfig.Builder, etc). Previously these configuration interfaces were inherited on the generated Config type but not on the configuration builder. This is pre-requisite for runtime plugins to work because without it every Config.Builder generated shares nothing with any other client. This prevents any kind of common code (e.g. like a plugin) to configure a service client without knowing the concrete type of client.
  • Rename ServiceGenerator to ServiceClientGenerator
  • Refactor ClientConfigGenerator into an abstract base class that makes less assumptions about the type of configuration being generated.
  • Generate a builder for service clients
  • Introduce new runtime types that generated service client companion objects inherit from. This moves the DSL operator invoke function into the runtime.
  • Introduce an abstract base class for service client builders. This will allow us to inject code into all clients instantiated (e.g. load plugins using SPI is one use case).
  • Moved a few things around (e.g. everything in core config package moved to the client subpackage).
  • Refactors RuntimeTypes to reduce boilerplate
  • Refactor our section generator abstraction to be type safe when getting section keys

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@@ -0,0 +1,336 @@
# Client Configuration

* **Type**: Convention
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: What's the difference between a convention and a design? Isn't convention merely ex-post facto design?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aren't we basically in "ex-post facto" phase at this point w.r.t to config?

Copy link
Contributor

Choose a reason for hiding this comment

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

We certainly are. I suppose my point was this feels like a design even if it's being written after the fact. It's even in the docs/design directory. I'm not sure what I would do differently after reading this document knowing it's a "convention" vs a "design".

If there's a clear difference in your mind we can leave it but this wording felt novel and conspicuous.

Comment on lines 12 to 13
This section describes general conventions and guidelines to be used for service client configuration. See the section
below for concrete example of these conventions in practice.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: "for a concrete example"

This section describes general conventions and guidelines to be used for service client configuration. See the section
below for concrete example of these conventions in practice.

* All generated service clients inherit from `SdkClientConfig.Builder`
Copy link
Contributor

Choose a reason for hiding this comment

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

Correctness: This statement isn't true. Perhaps this was meant to be one/all of the following:

  • All generated service clients implement SdkClient
  • All generated service client configs implement SdkClientConfig
  • All generated service client config builders implement SdkClientConfig.Builder

* SPDX-License-Identifier: Apache-2.0
*/
package aws.smithy.kotlin.runtime.config
package aws.smithy.kotlin.runtime.client
Copy link
Contributor

Choose a reason for hiding this comment

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

Correctness: Looks like the src classes changed packages but not the test class (and as a result the unit test no longer runs for me).

public val idempotencyTokenProvider: IdempotencyTokenProvider?
public val idempotencyTokenProvider: IdempotencyTokenProvider

public interface Builder {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Missing KDocs on public interface.

* Re-usable base class for generating some type that only contains configuration.
* e.g. roughly something shaped like below
*
* ```
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: ```kotlin

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think this matters for KDoc FWIW

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh great, nevermind then.

Comment on lines 53 to 54
val formattedBaseClasses = if (baseClasses.isNotEmpty()) ": $baseClasses" else ""
writer.openBlock("public class #configClass.name:L private constructor(builder: Builder)$formattedBaseClasses {")
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Missing a space before :. Ideally the class spec should look like:

public class FooConfig private constructor(builder: Builder) : BaseClass1, BaseClass2

Comment on lines 54 to 58
writer.openBlock("public class #configClass.name:L private constructor(builder: Builder)$formattedBaseClasses {")
.call { renderImmutableProperties(sortedProps, writer) }
.call { renderCompanionObject(writer) }
.call { renderBuilder(sortedProps, writer) }
.closeBlock("}")
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Can replace openBlock/closeBlock with withBlock.

Comment on lines 118 to 124
val baseClasses = props
.mapNotNull { it.builderBaseClass?.name }
.sorted()
.toSet()
.joinToString(", ")

val formattedBaseClasses = if (baseClasses.isNotEmpty()) ": $baseClasses" else ""
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Duplicated code, consider extracting to helper method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's nearly duplicated, uses different field from the property though. I'll take a look at cleaning it up.

*
* e.g.
*
* ```
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: ```kotlin

Copy link
Contributor

@lauzadis lauzadis left a comment

Choose a reason for hiding this comment

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

Looks good! I just had some non-blocking comments.

## Client Creation Patterns

This section describes in more detail how service clients are created and configured using the `BazClient` described
in [example service client](#example-service-client) section. These behaviors are a combination of runtime types and
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: "in the [example service client]"

```kotlin
// explicit using DSL syntax inherited from companion `SdkClientFactory`
val c1 = BazClient { // this: BazClient.Config.Builder
sdkLogMode = SdkLogMode.LogRequests
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: the SdkLogMode enum value is LogRequest instead of LogRequests


/**
* Context key for the service symbol
* SectionId used when rendering the service client builder
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: consider adding brackets around [SectionId] which adds a link to the definition, helpful when working in an IDE

Comment on lines 79 to 82
private fun clientContextConfigProps(trait: ClientContextParamsTrait): List<ConfigProperty> = buildList {
trait.parameters.forEach { (k, v) ->
add(
trait
.parameters
.map { (k, v) ->
Copy link
Contributor

Choose a reason for hiding this comment

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

Correctness: Remove buildList. As written, this always returns an empty list.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

NICE catch!

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 24 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@aajtodd aajtodd merged commit 36e612c into main Jan 20, 2023
@aajtodd aajtodd deleted the refactor-client-config branch January 20, 2023 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants