Skip to content

Commit 36b3e02

Browse files
authored
feat: add default initializer for service methods with no required inputs (#642)
1 parent cdab3f1 commit 36b3e02

File tree

7 files changed

+95
-7
lines changed

7 files changed

+95
-7
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "114d9286-0fe0-4d31-891b-fe0e8f1c9dcd",
3+
"type": "feature",
4+
"description": "Allow omission of input in service method calls where no parameters are required.",
5+
"issues": [
6+
"awslabs/smithy-kotlin#129"
7+
]
8+
}

smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/model/ShapeExt.kt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,25 @@ fun ServiceShape.hasIdempotentTokenMember(model: Model): Boolean {
107107

108108
/**
109109
* Return the formatted (Kotlin) function signature for the given operation
110-
*/
111-
fun OperationIndex.operationSignature(model: Model, symbolProvider: SymbolProvider, op: OperationShape): String {
110+
* @param includeOptionalDefault Set whether the arg declaration should include a default initializer for operations
111+
* where the input has no required parameters. Consumers that render both a super and subclass definition using this
112+
* method will need to manage this parameter to ensure that the default is only present in the former.
113+
*/
114+
fun OperationIndex.operationSignature(
115+
model: Model,
116+
symbolProvider: SymbolProvider,
117+
op: OperationShape,
118+
includeOptionalDefault: Boolean = false
119+
): String {
112120
val inputShape = this.getInput(op)
113121
val outputShape = this.getOutput(op)
114122
val input = inputShape.map { symbolProvider.toSymbol(it).name }
115123
val output = outputShape.map { symbolProvider.toSymbol(it).name }
116124

117125
val hasOutputStream = outputShape.map { it.hasStreamingMember(model) }.orElse(false)
118-
val inputParam = input.map { "input: $it" }.orElse("")
126+
val inputParam = input.map {
127+
if (includeOptionalDefault && inputShape.get().isOptional()) "input: $it = $it {}" else "input: $it"
128+
}.orElse("")
119129
val outputParam = output.map { ": $it" }.orElse("")
120130

121131
val operationName = op.defaultName()
@@ -220,3 +230,8 @@ fun UnionShape.filterEventStreamErrors(model: Model): Collection<MemberShape> {
220230
target.isError
221231
}
222232
}
233+
234+
/**
235+
* Test if a shape is optional.
236+
*/
237+
fun Shape.isOptional(): Boolean = members().none { it.isRequired }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class ServiceGenerator(private val ctx: RenderingContext<ServiceShape>) {
153153
writer.write("")
154154
writer.renderDocumentation(op)
155155
writer.renderAnnotations(op)
156-
writer.write(opIndex.operationSignature(ctx.model, ctx.symbolProvider, op))
156+
writer.write(opIndex.operationSignature(ctx.model, ctx.symbolProvider, op, includeOptionalDefault = true))
157157

158158
// Add DSL overload (if appropriate)
159159
opIndex.getInput(op).ifPresent { inputShape ->

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@ class ServiceGeneratorTest {
5050
fun `it renders signatures correctly`() {
5151
val expectedSignatures = listOf(
5252
"suspend fun getFoo(input: GetFooRequest): GetFooResponse",
53-
"suspend fun getFooNoInput(input: GetFooNoInputRequest): GetFooNoInputResponse",
53+
"suspend fun getFooNoRequired(input: GetFooNoRequiredRequest = GetFooNoRequiredRequest {}): GetFooNoRequiredResponse",
54+
"suspend fun getFooSomeRequired(input: GetFooSomeRequiredRequest): GetFooSomeRequiredResponse",
55+
"suspend fun getFooNoInput(input: GetFooNoInputRequest = GetFooNoInputRequest {}): GetFooNoInputResponse",
5456
"suspend fun getFooNoOutput(input: GetFooNoOutputRequest): GetFooNoOutputResponse",
5557
"suspend fun getFooStreamingInput(input: GetFooStreamingInputRequest): GetFooStreamingInputResponse",
5658
"suspend fun <T> getFooStreamingOutput(input: GetFooStreamingOutputRequest, block: suspend (GetFooStreamingOutputResponse) -> T): T",
57-
"suspend fun <T> getFooStreamingOutputNoInput(input: GetFooStreamingOutputNoInputRequest, block: suspend (GetFooStreamingOutputNoInputResponse) -> T): T",
59+
"suspend fun <T> getFooStreamingOutputNoInput(input: GetFooStreamingOutputNoInputRequest = GetFooStreamingOutputNoInputRequest {}, block: suspend (GetFooStreamingOutputNoInputResponse) -> T): T",
5860
"suspend fun getFooStreamingInputNoOutput(input: GetFooStreamingInputNoOutputRequest): GetFooStreamingInputNoOutputResponse"
5961
)
6062
expectedSignatures.forEach {

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,36 @@ class HttpProtocolClientGeneratorTest {
177177
op.install(MockMiddleware(configurationField1 = "testing"))
178178
return op.roundTrip(client, input)
179179
}
180+
""",
181+
"""
182+
override suspend fun getFooNoRequired(input: GetFooNoRequiredRequest): GetFooNoRequiredResponse {
183+
val op = SdkHttpOperation.build<GetFooNoRequiredRequest, GetFooNoRequiredResponse> {
184+
serializer = GetFooNoRequiredOperationSerializer()
185+
deserializer = GetFooNoRequiredOperationDeserializer()
186+
context {
187+
expectedHttpStatus = 200
188+
service = serviceName
189+
operationName = "GetFooNoRequired"
190+
}
191+
}
192+
op.install(MockMiddleware(configurationField1 = "testing"))
193+
return op.roundTrip(client, input)
194+
}
195+
""",
196+
"""
197+
override suspend fun getFooSomeRequired(input: GetFooSomeRequiredRequest): GetFooSomeRequiredResponse {
198+
val op = SdkHttpOperation.build<GetFooSomeRequiredRequest, GetFooSomeRequiredResponse> {
199+
serializer = GetFooSomeRequiredOperationSerializer()
200+
deserializer = GetFooSomeRequiredOperationDeserializer()
201+
context {
202+
expectedHttpStatus = 200
203+
service = serviceName
204+
operationName = "GetFooSomeRequired"
205+
}
206+
}
207+
op.install(MockMiddleware(configurationField1 = "testing"))
208+
return op.roundTrip(client, input)
209+
}
180210
"""
181211
)
182212
expectedBodies.forEach {

smithy-kotlin-codegen/src/test/resources/software/amazon/smithy/kotlin/codegen/service-generator-deprecated.smithy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ operation YeOldeOperation {
2020

2121
@deprecated
2222
structure YeOldeOperationRequest {
23+
@required
2324
@deprecated
2425
@httpQuery("yeOldeParam")
2526
yeOldParameter: String,

smithy-kotlin-codegen/src/test/resources/software/amazon/smithy/kotlin/codegen/service-generator-test-operations.smithy

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ service Test {
88
version: "1.0.0",
99
operations: [
1010
GetFoo,
11+
GetFooNoRequired,
12+
GetFooSomeRequired,
1113
GetFooNoInput,
1214
GetFooNoOutput,
1315
GetFooStreamingInput,
@@ -25,12 +27,41 @@ operation GetFoo {
2527
errors: [GetFooError]
2628
}
2729

28-
structure GetFooRequest {}
30+
structure GetFooRequest {
31+
@required
32+
@httpQuery("q")
33+
query: String
34+
}
2935
structure GetFooResponse {}
3036

3137
@error("client")
3238
structure GetFooError {}
3339

40+
@http(method: "GET", uri: "/foo-no-required")
41+
operation GetFooNoRequired {
42+
input: GetFooNoRequiredRequest,
43+
output: GetFooResponse
44+
}
45+
46+
structure GetFooNoRequiredRequest {
47+
@httpQuery("q0")
48+
q0: String,
49+
@httpQuery("q1")
50+
q1: String
51+
}
52+
53+
@http(method: "GET", uri: "/foo-some-required")
54+
operation GetFooSomeRequired {
55+
input: GetFooSomeRequiredRequest,
56+
output: GetFooResponse
57+
}
58+
59+
structure GetFooSomeRequiredRequest {
60+
@required @httpQuery("q0")
61+
q0: String,
62+
@httpQuery("q1")
63+
q1: String
64+
}
3465

3566
@http(method: "GET", uri: "/foo-no-input")
3667
operation GetFooNoInput {
@@ -46,6 +77,7 @@ operation GetFooNoOutput {
4677
blob BodyStream
4778

4879
structure GetFooStreamingRequest {
80+
@required
4981
body: BodyStream
5082
}
5183

0 commit comments

Comments
 (0)