Skip to content

Commit c5ce0a8

Browse files
lauzadisianbotsf
andauthored
misc!: v1.2.0 (#1079)
* fix: apply clock skew interceptor to clients created via `invoke` (#1067) * bumping version to 1.2.0 * whoops wait, don't bump versions manually; use the changelog mechanism 'requiresMinorVersionBump' * misc: upgrade to OkHttp 5.0.0-alpha.14, Okio 3.9.0, Kotlin 1.9.23 (#1070) * fix: remove draft (#1073) --------- Co-authored-by: Ian Botsford <[email protected]>
1 parent 59c63da commit c5ce0a8

File tree

11 files changed

+138
-69
lines changed

11 files changed

+138
-69
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"id": "5498a35b-f57f-4c0c-bbf7-43e07830be01",
3+
"type": "bugfix",
4+
"description": "⚠️ **IMPORTANT**: Add config finalization to service clients via new abstract factory class; apply clock skew interceptor to clients created via `invoke`",
5+
"issues": [
6+
"awslabs/aws-sdk-kotlin#1211"
7+
],
8+
"requiresMinorVersionBump": true
9+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"id": "8e87c6dd-5138-4b79-a0c2-255ecf31898b",
3+
"type": "misc",
4+
"description": "⚠️ **IMPORTANT**: Upgrade to latest versions of OkHttp, Okio, Kotlin",
5+
"requiresMinorVersionBump": true
6+
}

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/middleware/ClockSkew.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ import software.amazon.smithy.kotlin.codegen.rendering.ServiceClientGenerator
1515
*/
1616
class ClockSkew : KotlinIntegration {
1717
override val sectionWriters: List<SectionWriterBinding>
18-
get() = listOf(SectionWriterBinding(ServiceClientGenerator.Sections.FinalizeConfig, clockSkewSectionWriter))
18+
get() = listOf(
19+
SectionWriterBinding(
20+
ServiceClientGenerator.Sections.CompanionObject.FinalizeConfig,
21+
clockSkewSectionWriter,
22+
),
23+
)
1924

2025
private val clockSkewSectionWriter = AppendingSectionWriter { writer ->
2126
val interceptorSymbol = RuntimeTypes.AwsProtocolCore.ClockSkewInterceptor

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ object RuntimeTypes {
190190
object SmithyClient : RuntimeTypePackage(KotlinDependency.SMITHY_CLIENT) {
191191
val SdkClient = symbol("SdkClient")
192192
val AbstractSdkClientBuilder = symbol("AbstractSdkClientBuilder")
193+
val AbstractSdkClientFactory = symbol("AbstractSdkClientFactory")
193194
val LogMode = symbol("LogMode")
194195
val RetryClientConfig = symbol("RetryClientConfig")
195196
val RetryStrategyClientConfig = symbol("RetryStrategyClientConfig")

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ServiceClientGenerator.kt

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {
3737
* [SectionId] used when rendering the service interface companion object
3838
*/
3939
object CompanionObject : SectionId {
40+
41+
/**
42+
* [SectionId] used when rendering the finalizeConfig block of a service client companion object
43+
*/
44+
object FinalizeConfig : SectionId
45+
4046
/**
4147
* Context key for the service symbol
4248
*/
@@ -46,6 +52,11 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {
4652
* Context key for the SDK ID
4753
*/
4854
val SdkId: SectionKey<String> = SectionKey("SdkId")
55+
56+
/**
57+
* [SectionId] used when rendering the supertype(s) of the companion object
58+
*/
59+
object SuperTypes : SectionId
4960
}
5061

5162
/**
@@ -57,11 +68,6 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {
5768
*/
5869
val RenderingContext: SectionKey<RenderingContext<ServiceShape>> = SectionKey("RenderingContext")
5970
}
60-
61-
/**
62-
* [SectionId] used when rendering the finalizeConfig block of a service client
63-
*/
64-
object FinalizeConfig : SectionId
6571
}
6672

6773
init {
@@ -88,38 +94,28 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {
8894

8995
writer.renderDocumentation(service)
9096
writer.renderAnnotations(service)
91-
writer.openBlock(
97+
writer.withBlock(
9298
"#L interface ${serviceSymbol.name} : #T {",
99+
"}",
93100
ctx.settings.api.visibility,
94101
RuntimeTypes.SmithyClient.SdkClient,
95-
)
96-
.call {
97-
// allow access to client's Config
98-
writer.dokka("${serviceSymbol.name}'s configuration")
99-
writer.write("public override val config: Config")
100-
}
101-
.call {
102-
// allow integrations to add additional fields to companion object or configuration
103-
writer.write("")
104-
writer.declareSection(
105-
Sections.CompanionObject,
106-
context = mapOf(
107-
Sections.CompanionObject.ServiceSymbol to serviceSymbol,
108-
Sections.CompanionObject.SdkId to ctx.settings.sdkId,
109-
),
110-
) {
111-
renderCompanionObject()
112-
}
113-
writer.write("")
114-
renderServiceBuilder()
102+
) {
103+
// allow access to client's Config
104+
dokka("${serviceSymbol.name}'s configuration")
105+
write("public override val config: Config")
115106

116-
writer.write("")
117-
renderServiceConfig()
118-
}
119-
.call {
120-
operations.forEach { renderOperation(operationsIndex, it) }
121-
}
122-
.closeBlock("}")
107+
// allow integrations to add additional fields to companion object or configuration
108+
write("")
109+
renderCompanionObject()
110+
111+
write("")
112+
renderServiceBuilder()
113+
114+
write("")
115+
renderServiceConfig()
116+
117+
operations.forEach { renderOperation(operationsIndex, it) }
118+
}
123119
.write("")
124120

125121
if (ctx.protocolGenerator != null) { // returns default impl, which only exists if there's a protocol generator
@@ -172,15 +168,39 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {
172168
private fun renderCompanionObject() {
173169
// don't render a companion object which is used for building a service client unless we have a protocol generator
174170
if (ctx.protocolGenerator == null) return
175-
writer.withBlock(
176-
"public companion object : #T<Config, Config.Builder, #T, Builder> {",
177-
"}",
178-
RuntimeTypes.SmithyClient.SdkClientFactory,
179-
serviceSymbol,
180-
) {
181-
write("@#T", KotlinTypes.Jvm.JvmStatic)
182-
write("override fun builder(): Builder = Builder()")
183-
}
171+
172+
writer
173+
.writeInline("public companion object : ")
174+
.declareSection(
175+
Sections.CompanionObject.SuperTypes,
176+
context = mapOf(
177+
Sections.CompanionObject.ServiceSymbol to serviceSymbol,
178+
),
179+
) {
180+
writeInline(
181+
"#T<Config, Config.Builder, #T, Builder>()",
182+
RuntimeTypes.SmithyClient.AbstractSdkClientFactory,
183+
serviceSymbol,
184+
)
185+
}
186+
.withBlock(" {", "}") {
187+
declareSection(
188+
Sections.CompanionObject,
189+
context = mapOf(
190+
Sections.CompanionObject.ServiceSymbol to serviceSymbol,
191+
Sections.CompanionObject.SdkId to ctx.settings.sdkId,
192+
),
193+
) {
194+
write("@#T", KotlinTypes.Jvm.JvmStatic)
195+
write("override fun builder(): Builder = Builder()")
196+
write("")
197+
withBlock("override fun finalizeConfig(builder: Builder) {", "}") {
198+
declareSection(Sections.CompanionObject.FinalizeConfig) {
199+
write("super.finalizeConfig(builder)")
200+
}
201+
}
202+
}
203+
}
184204
}
185205

186206
private fun renderOperation(opIndex: OperationIndex, op: OperationShape) {

codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ServiceClientGeneratorTest.kt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@ class ServiceClientGeneratorTest {
5555
@Test
5656
fun `it renders a companion object with default client factory if protocol generator`() {
5757
val expected = """
58-
public companion object : SdkClientFactory<Config, Config.Builder, TestClient, Builder> {
58+
public companion object : AbstractSdkClientFactory<Config, Config.Builder, TestClient, Builder>() {
5959
@JvmStatic
6060
override fun builder(): Builder = Builder()
61+
62+
override fun finalizeConfig(builder: Builder) {
63+
super.finalizeConfig(builder)
64+
}
6165
}
6266
""".formatForTest()
6367
commonWithProtocolTestContents.shouldContainOnlyOnceWithDiff(expected)
@@ -66,7 +70,7 @@ class ServiceClientGeneratorTest {
6670
@Test
6771
fun `it renders a companion object without default client factory if no protocol generator`() {
6872
val expected = """
69-
public companion object : SdkClientFactory
73+
public companion object : AbstractSdkClientFactory
7074
""".formatForTest()
7175
commonTestContents.shouldNotContain(expected)
7276
}
@@ -91,9 +95,7 @@ class ServiceClientGeneratorTest {
9195
val writer = KotlinWriter(TestModelDefault.NAMESPACE)
9296
val service = model.expectShape<ServiceShape>(TestModelDefault.SERVICE_SHAPE_ID)
9397
writer.registerSectionWriter(ServiceClientGenerator.Sections.CompanionObject) { codeWriter, _ ->
94-
codeWriter.openBlock("public companion object {")
95-
.write("public fun foo(): Int = 1")
96-
.closeBlock("}")
98+
codeWriter.write("public fun foo(): Int = 1")
9799
}
98100

99101
writer.registerSectionWriter(ServiceClientGenerator.Sections.ServiceConfig) { codeWriter, _ ->
@@ -103,13 +105,17 @@ class ServiceClientGeneratorTest {
103105
}
104106

105107
val settings = KotlinSettings(service.id, KotlinSettings.PackageSettings("test", "0.0"), sdkId = service.id.name)
106-
val renderingCtx = RenderingContext(writer, service, model, provider, settings)
108+
109+
// Without a protocol generator the companion object won't be generated so use a fake protocol here.
110+
val protocol = MockHttpProtocolGenerator(model)
111+
val renderingCtx = RenderingContext(writer, service, model, provider, settings, protocol)
112+
107113
val generator = ServiceClientGenerator(renderingCtx)
108114
generator.render()
109115
val contents = writer.toString()
110116

111117
val expectedCompanionOverride = """
112-
public companion object {
118+
public companion object : AbstractSdkClientFactory<Config, Config.Builder, TestClient, Builder>() {
113119
public fun foo(): Int = 1
114120
}
115121
""".formatForTest()

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
[versions]
2-
kotlin-version = "1.9.22"
2+
kotlin-version = "1.9.23"
33
dokka-version = "1.9.10"
44

55
aws-kotlin-repo-tools-version = "0.4.0"
66

77
# libs
88
coroutines-version = "1.7.3"
99
atomicfu-version = "0.23.1"
10-
okhttp-version = "5.0.0-alpha.11"
11-
okio-version = "3.6.0"
10+
okhttp-version = "5.0.0-alpha.14"
11+
okio-version = "3.9.0"
1212
otel-version = "1.32.0"
1313
slf4j-version = "2.0.9"
1414
slf4j-v1x-version = "1.7.36"

runtime/protocol/http-client-engines/http-client-engine-okhttp/jvm/src/aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpEngine.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import aws.smithy.kotlin.runtime.net.TlsVersion
1414
import aws.smithy.kotlin.runtime.operation.ExecutionContext
1515
import aws.smithy.kotlin.runtime.time.Instant
1616
import aws.smithy.kotlin.runtime.time.fromEpochMilliseconds
17+
import kotlinx.coroutines.ExperimentalCoroutinesApi
1718
import kotlinx.coroutines.job
1819
import okhttp3.*
20+
import okhttp3.coroutines.executeAsync
1921
import java.util.concurrent.TimeUnit
2022
import kotlin.time.toJavaDuration
2123
import aws.smithy.kotlin.runtime.net.TlsVersion as SdkTlsVersion
@@ -48,6 +50,8 @@ public class OkHttpEngine(
4850

4951
val engineRequest = request.toOkHttpRequest(context, callContext, metrics)
5052
val engineCall = client.newCall(engineRequest)
53+
54+
@OptIn(ExperimentalCoroutinesApi::class)
5155
val engineResponse = mapOkHttpExceptions { engineCall.executeAsync() }
5256

5357
val response = engineResponse.toSdkResponse()

runtime/protocol/http-client-engines/http-client-engine-okhttp/jvm/test/aws/smithy/kotlin/runtime/http/engine/okhttp/StreamingRequestBodyTest.kt

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import aws.smithy.kotlin.runtime.text.encoding.encodeToHex
1313
import kotlinx.coroutines.*
1414
import kotlinx.coroutines.test.runTest
1515
import okio.Buffer
16-
import okio.BufferedSink
1716
import org.junit.jupiter.api.Test
1817
import kotlin.coroutines.EmptyCoroutineContext
1918
import kotlin.test.*
@@ -134,7 +133,7 @@ class StreamingRequestBodyTest {
134133
override fun readFrom(): SdkByteReadChannel = chan
135134
}
136135

137-
val sink = TestSink()
136+
val sink = Buffer()
138137

139138
val callJob = Job()
140139
val callContext = coroutineContext + callJob
@@ -145,19 +144,14 @@ class StreamingRequestBodyTest {
145144
assertEquals(0, callJob.children.toList().size)
146145
actual.writeTo(sink)
147146
assertEquals(1, callJob.children.toList().size) // writer
148-
assertEquals(sink.buffer.size, 0)
147+
assertEquals(sink.size, 0)
149148
chan.writeAll(content.source())
150149

151-
assertFalse(sink.isClosed)
152-
153150
chan.close()
154151
callJob.complete()
155152
callJob.join()
156153

157-
// we must manually close the sink given to us when stream completes
158-
assertTrue(sink.isClosed)
159-
160-
val actualSha256 = sink.buffer.sha256().hex()
154+
val actualSha256 = sink.sha256().hex()
161155
assertEquals(expectedSha256, actualSha256)
162156
}
163157

@@ -174,14 +168,9 @@ class StreamingRequestBodyTest {
174168
val callContext = coroutineContext + Job()
175169
val actual = StreamingRequestBody(body, callContext)
176170

177-
val sink = TestSink()
171+
val sink = Buffer()
178172
actual.writeTo(sink)
179173

180-
assertContentEquals(file.readBytes(), sink.buffer.readByteArray())
174+
assertContentEquals(file.readBytes(), sink.readByteArray())
181175
}
182176
}
183-
184-
private class TestSink(override val buffer: Buffer = Buffer()) : BufferedSink by buffer {
185-
var isClosed = false
186-
override fun close() { isClosed = true }
187-
}

runtime/smithy-client/api/smithy-client.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ public abstract class aws/smithy/kotlin/runtime/client/AbstractSdkClientBuilder
55
protected abstract fun newClient (Laws/smithy/kotlin/runtime/client/SdkClientConfig;)Laws/smithy/kotlin/runtime/client/SdkClient;
66
}
77

8+
public abstract class aws/smithy/kotlin/runtime/client/AbstractSdkClientFactory : aws/smithy/kotlin/runtime/client/SdkClientFactory {
9+
public fun <init> ()V
10+
protected fun finalizeConfig (Laws/smithy/kotlin/runtime/client/SdkClient$Builder;)V
11+
public fun invoke (Lkotlin/jvm/functions/Function1;)Laws/smithy/kotlin/runtime/client/SdkClient;
12+
}
13+
814
public abstract interface class aws/smithy/kotlin/runtime/client/IdempotencyTokenConfig {
915
public abstract fun getIdempotencyTokenProvider ()Laws/smithy/kotlin/runtime/client/IdempotencyTokenProvider;
1016
}

0 commit comments

Comments
 (0)