Skip to content

Commit b641b9f

Browse files
committed
Generate an input shape with additionalInput member for operations with Unit input
Fixes #863
1 parent a76837c commit b641b9f

File tree

2 files changed

+110
-5
lines changed

2 files changed

+110
-5
lines changed

model-bundle/model-bundle-api/src/main/java/software/amazon/smithy/modelbundle/api/ModelBundles.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ private ModelBundles() {}
2525
private static final PluginProviders PLUGIN_PROVIDERS = PluginProviders.builder().build();
2626

2727
public static Service getService(SmithyBundle smithyBundle) {
28-
var model = getModel(smithyBundle);
28+
var model = prepareModelForBundling(smithyBundle);
2929
var plugin = PLUGIN_PROVIDERS.getPlugin(smithyBundle.getConfigType(), smithyBundle.getConfig());
3030
return ProxyService.builder()
3131
.model(model)
@@ -35,7 +35,8 @@ public static Service getService(SmithyBundle smithyBundle) {
3535
.build();
3636
}
3737

38-
private static Model getModel(SmithyBundle bundle) {
38+
// visible for testing
39+
static Model prepareModelForBundling(SmithyBundle bundle) {
3940
// TODO: model the type in the returned bundle
4041
var suffix = bundle.getModel().startsWith("$version") ? "smithy" : "json";
4142
var modelAssemble = new ModelAssembler().putProperty(ModelAssembler.ALLOW_UNKNOWN_TRAITS, true)
@@ -49,7 +50,6 @@ private static Model getModel(SmithyBundle bundle) {
4950
model = modelAssemble.assemble().unwrap();
5051
additionalInputShape =
5152
model.expectShape(ShapeId.from(additionalInput.getIdentifier())).asStructureShape().get();
52-
5353
} else {
5454
model = modelAssemble.assemble().unwrap();
5555
}
@@ -116,16 +116,24 @@ private static void addProxyOperationWithAdditionalInput(
116116
var input = op.getInput();
117117
StructureShape finalInput;
118118
if (op.getInput().isEmpty()) {
119-
finalInput = additionalInput;
119+
var containerId = syntheticContainerForInput(additionalInput);
120+
var container = builder.getCurrentShapes().get(containerId);
121+
if (container == null) {
122+
finalInput = createSyntheticInput(containerId, additionalInput);
123+
builder.addShape(finalInput);
124+
} else {
125+
finalInput = (StructureShape) container;
126+
}
120127
} else {
121128
var inputBuilder = model.expectShape(input.get(), StructureShape.class).toBuilder();
122129
inputBuilder.addMember(MemberShape.builder()
123130
.id(ShapeId.from(inputBuilder.getId().toString() + "$additionalInput"))
124131
.target(additionalInput.getId())
125132
.build());
126133
finalInput = inputBuilder.id(ShapeId.from(inputBuilder.getId().toString()) + "Proxy").build();
134+
builder.addShape(finalInput);
127135
}
128-
builder.addShape(finalInput);
136+
129137
var newOperation = op.toBuilder()
130138
.id(ShapeId.from(op.getId().toString() + "Proxy"))
131139
.input(finalInput)
@@ -135,4 +143,18 @@ private static void addProxyOperationWithAdditionalInput(
135143
builder.addShape(newOperation);
136144
serviceBuilder.addOperation(newOperation).build();
137145
}
146+
147+
private static ShapeId syntheticContainerForInput(StructureShape additionalInput) {
148+
return ShapeId.from("smithy.mcp#AdditionalInputFor" + additionalInput.getId().getName());
149+
}
150+
151+
private static StructureShape createSyntheticInput(ShapeId containerId, StructureShape additionalInput) {
152+
return StructureShape.builder()
153+
.id(containerId)
154+
.addMember(MemberShape.builder()
155+
.id(containerId.toString() + "$additionalInput")
156+
.target(additionalInput.getId())
157+
.build())
158+
.build();
159+
}
138160
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.modelbundle.api;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertTrue;
10+
11+
import org.junit.jupiter.api.Test;
12+
import software.amazon.smithy.java.core.serde.document.Document;
13+
import software.amazon.smithy.model.shapes.ShapeId;
14+
import software.amazon.smithy.model.shapes.StructureShape;
15+
import software.amazon.smithy.modelbundle.api.model.AdditionalInput;
16+
import software.amazon.smithy.modelbundle.api.model.SmithyBundle;
17+
18+
class ModelBundlesTest {
19+
20+
@Test
21+
void testOperationWithNoInputGetsSyntheticAdditionalInputShape() {
22+
String smithyModel = """
23+
$version: "2.0"
24+
25+
namespace com.example
26+
27+
service TestService {
28+
version: "1.0"
29+
operations: [TestOperation]
30+
}
31+
32+
operation TestOperation {
33+
output: TestOutput
34+
}
35+
36+
structure TestOutput {
37+
result: String
38+
}
39+
""";
40+
41+
String additionalInputModel = """
42+
$version: "2.0"
43+
44+
namespace com.example.additional
45+
46+
structure AdditionalInputData {
47+
context: String
48+
}
49+
""";
50+
51+
AdditionalInput additionalInput = AdditionalInput.builder()
52+
.identifier("com.example.additional#AdditionalInputData")
53+
.model(additionalInputModel)
54+
.build();
55+
56+
SmithyBundle bundle = SmithyBundle.builder()
57+
.model(smithyModel)
58+
.serviceName("com.example#TestService")
59+
.additionalInput(additionalInput)
60+
.configType("configType")
61+
.config(Document.ofObject(null))
62+
.build();
63+
64+
var model = ModelBundles.prepareModelForBundling(bundle);
65+
66+
ShapeId syntheticInputId = ShapeId.from("smithy.mcp#AdditionalInputForAdditionalInputData");
67+
assertTrue(model.getShape(syntheticInputId).isPresent());
68+
var syntheticInputShape = model.expectShape(syntheticInputId, StructureShape.class);
69+
assertEquals(1, syntheticInputShape.members().size());
70+
assertTrue(syntheticInputShape.getMember("additionalInput").isPresent());
71+
var additionalInputMember = syntheticInputShape.getMember("additionalInput").get();
72+
assertEquals(ShapeId.from("com.example.additional#AdditionalInputData"),
73+
additionalInputMember.getTarget());
74+
75+
ShapeId proxyOperationId = ShapeId.from("com.example#TestOperationProxy");
76+
assertTrue(model.getShape(proxyOperationId).isPresent());
77+
78+
var proxyOperation = model.expectShape(proxyOperationId).asOperationShape().get();
79+
assertTrue(proxyOperation.getInput().isPresent());
80+
81+
assertEquals(syntheticInputId, proxyOperation.getInputShape());
82+
}
83+
}

0 commit comments

Comments
 (0)