Skip to content

Commit f994059

Browse files
Fix and validate synthetic service
This adds a validation step to generating a synthetic service to make sure we aren't generating anything too crazy. It also resolves some validation issues that cropped up in the integ test.
1 parent 47ef13b commit f994059

File tree

3 files changed

+100
-67
lines changed

3 files changed

+100
-67
lines changed

codegen/plugins/types/src/it/java/software/amazon/smithy/python/codegen/types/PythonTypeCodegenTest.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@ public void testCodegenWithoutService(@TempDir Path tempDir) {
4949
.fileManifest(FileManifest.create(tempDir))
5050
.settings(
5151
ObjectNode.builder()
52-
.withMember("selector", """
53-
:test([id|namespace = 'example.weather'],
54-
[id|namespace = 'example.weather.nested'],
55-
[id|namespace = 'example.weather.nested.more'])
56-
""")
5752
.withMember("module", "types_test")
5853
.withMember("moduleVersion", "0.0.1")
5954
.build())
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.python.codegen.types;
6+
7+
import java.util.HashSet;
8+
import java.util.Set;
9+
import software.amazon.smithy.model.Model;
10+
import software.amazon.smithy.model.loader.Prelude;
11+
import software.amazon.smithy.model.selector.Selector;
12+
import software.amazon.smithy.model.shapes.OperationShape;
13+
import software.amazon.smithy.model.shapes.ServiceShape;
14+
import software.amazon.smithy.model.shapes.Shape;
15+
import software.amazon.smithy.model.shapes.ShapeId;
16+
import software.amazon.smithy.model.shapes.ShapeType;
17+
import software.amazon.smithy.model.shapes.StructureShape;
18+
import software.amazon.smithy.model.traits.ErrorTrait;
19+
import software.amazon.smithy.model.traits.InputTrait;
20+
import software.amazon.smithy.model.traits.MixinTrait;
21+
import software.amazon.smithy.model.traits.OutputTrait;
22+
import software.amazon.smithy.model.traits.PrivateTrait;
23+
import software.amazon.smithy.model.traits.ProtocolDefinitionTrait;
24+
import software.amazon.smithy.model.traits.TraitDefinition;
25+
import software.amazon.smithy.model.transform.ModelTransformer;
26+
27+
final class CreateSyntheticService {
28+
private static final String SYNTHETIC_NAMESPACE = "smithy.synthetic";
29+
private static final String SYNTHETIC_OPERATION_NAME = "TypesGenOperation";
30+
static final ShapeId SYNTHETIC_SERVICE_ID = ShapeId.fromParts(SYNTHETIC_NAMESPACE, "TypesGenService");
31+
private static final Set<ShapeType> GENERATED_TYPES = Set.of(
32+
ShapeType.STRUCTURE,
33+
ShapeType.UNION,
34+
ShapeType.ENUM,
35+
ShapeType.INT_ENUM);
36+
37+
private final Selector selector;
38+
private final boolean includeInputsAndOutputs;
39+
40+
CreateSyntheticService(Selector selector, boolean includeInputsAndOutputs) {
41+
this.selector = selector;
42+
this.includeInputsAndOutputs = includeInputsAndOutputs;
43+
}
44+
45+
Model transform(ModelTransformer transformer, Model model) {
46+
var addedShapes = new HashSet<Shape>();
47+
ServiceShape.Builder serviceBuilder = ServiceShape.builder()
48+
.id(SYNTHETIC_SERVICE_ID);
49+
50+
var index = 0;
51+
for (Shape shape : selector.select(model)) {
52+
if (!shouldGenerate(shape)) {
53+
continue;
54+
}
55+
56+
var operationId = ShapeId.fromParts(SYNTHETIC_NAMESPACE, SYNTHETIC_OPERATION_NAME + index);
57+
var inputId = ShapeId.fromParts(SYNTHETIC_NAMESPACE, operationId.getName() + "Input");
58+
var inputBuilder = StructureShape.builder()
59+
.id(inputId)
60+
.addTrait(new InputTrait());
61+
var operationBuilder = OperationShape.builder().id(operationId).input(inputId);
62+
serviceBuilder.addOperation(operationId);
63+
64+
if (shape.hasTrait(ErrorTrait.class)) {
65+
operationBuilder.addError(shape.getId());
66+
} else {
67+
inputBuilder.addMember("member", shape.getId());
68+
index++;
69+
}
70+
addedShapes.add(inputBuilder.build());
71+
addedShapes.add(operationBuilder.build());
72+
}
73+
74+
addedShapes.add(serviceBuilder.build());
75+
model = transformer.replaceShapes(model, addedShapes);
76+
77+
// Ensure validation gets run so we aren't generating anything too crazy
78+
Model.assembler().addModel(model).assemble().validate();
79+
return model;
80+
}
81+
82+
private boolean shouldGenerate(Shape shape) {
83+
if (!GENERATED_TYPES.contains(shape.getType())
84+
|| shape.hasTrait(MixinTrait.class)
85+
|| shape.hasTrait(ProtocolDefinitionTrait.class)
86+
|| shape.hasTrait(TraitDefinition.class)
87+
|| shape.hasTrait(PrivateTrait.class)
88+
|| Prelude.isPreludeShape(shape)) {
89+
return false;
90+
}
91+
92+
return includeInputsAndOutputs
93+
|| (!shape.hasTrait(InputTrait.class) && !shape.hasTrait(OutputTrait.class));
94+
}
95+
}

codegen/plugins/types/src/main/java/software/amazon/smithy/python/codegen/types/PythonTypeCodegenPlugin.java

Lines changed: 5 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,18 @@
44
*/
55
package software.amazon.smithy.python.codegen.types;
66

7-
import java.util.Collection;
8-
import java.util.Set;
7+
import static software.amazon.smithy.python.codegen.types.CreateSyntheticService.SYNTHETIC_SERVICE_ID;
8+
99
import software.amazon.smithy.build.PluginContext;
1010
import software.amazon.smithy.build.SmithyBuildPlugin;
1111
import software.amazon.smithy.codegen.core.directed.CodegenDirector;
12-
import software.amazon.smithy.model.Model;
13-
import software.amazon.smithy.model.loader.Prelude;
14-
import software.amazon.smithy.model.shapes.OperationShape;
15-
import software.amazon.smithy.model.shapes.ServiceShape;
16-
import software.amazon.smithy.model.shapes.Shape;
17-
import software.amazon.smithy.model.shapes.ShapeId;
18-
import software.amazon.smithy.model.shapes.ShapeType;
19-
import software.amazon.smithy.model.shapes.StructureShape;
20-
import software.amazon.smithy.model.traits.ErrorTrait;
21-
import software.amazon.smithy.model.traits.InputTrait;
22-
import software.amazon.smithy.model.traits.MixinTrait;
23-
import software.amazon.smithy.model.traits.OutputTrait;
2412
import software.amazon.smithy.model.transform.ModelTransformer;
2513
import software.amazon.smithy.python.codegen.GenerationContext;
2614
import software.amazon.smithy.python.codegen.PythonSettings;
2715
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
2816
import software.amazon.smithy.python.codegen.writer.PythonWriter;
2917

3018
public final class PythonTypeCodegenPlugin implements SmithyBuildPlugin {
31-
private static final String SYNTHETIC_NAMESPACE = "smithy.synthetic";
32-
private static final ShapeId SYNTHETIC_SERVICE_ID = ShapeId.fromParts(SYNTHETIC_NAMESPACE, "TypesGenService");
33-
private static final ShapeId SYNTHETIC_OPERATION_ID = ShapeId.fromParts(SYNTHETIC_NAMESPACE, "TypesGenOperation");
34-
private static final ShapeId SYNTHETIC_INPUT_ID = ShapeId.fromParts(SYNTHETIC_NAMESPACE, "TypesGenOperationInput");
35-
private static final Set<ShapeType> GENERATED_TYPES = Set.of(
36-
ShapeType.STRUCTURE,
37-
ShapeType.UNION,
38-
ShapeType.ENUM,
39-
ShapeType.INT_ENUM);
4019

4120
@Override
4221
public String getName() {
@@ -54,7 +33,9 @@ public void execute(PluginContext context) {
5433

5534
var model = context.getModel();
5635
if (typeSettings.service().isEmpty()) {
57-
model = addSyntheticService(model, typeSettings.selector().select(model), typeSettings);
36+
var transformer =
37+
new CreateSyntheticService(typeSettings.selector(), typeSettings.generateInputsAndOutputs());
38+
model = transformer.transform(ModelTransformer.create(), model);
5839
}
5940

6041
runner.settings(pythonSettings);
@@ -66,42 +47,4 @@ public void execute(PluginContext context) {
6647
runner.performDefaultCodegenTransforms();
6748
runner.run();
6849
}
69-
70-
private Model addSyntheticService(Model model, Collection<Shape> shapes, PythonTypeCodegenSettings settings) {
71-
StructureShape.Builder inputBuilder = StructureShape.builder()
72-
.id(SYNTHETIC_INPUT_ID)
73-
.addTrait(new InputTrait());
74-
75-
OperationShape.Builder operationBuilder = OperationShape.builder()
76-
.id(SYNTHETIC_OPERATION_ID)
77-
.input(SYNTHETIC_INPUT_ID);
78-
79-
ServiceShape.Builder serviceBuilder = ServiceShape.builder()
80-
.id(SYNTHETIC_SERVICE_ID);
81-
82-
var index = 0;
83-
for (Shape shape : shapes) {
84-
if (!GENERATED_TYPES.contains(shape.getType())
85-
|| shape.hasTrait(MixinTrait.class)
86-
|| Prelude.isPreludeShape(shape)) {
87-
continue;
88-
}
89-
90-
if (!settings.generateInputsAndOutputs()
91-
&& (shape.hasTrait(InputTrait.class) || shape.hasTrait(OutputTrait.class))) {
92-
continue;
93-
}
94-
95-
if (shape.hasTrait(ErrorTrait.class)) {
96-
operationBuilder.addError(shape.getId());
97-
} else {
98-
inputBuilder.addMember("member" + index, shape.getId());
99-
index++;
100-
}
101-
}
102-
103-
var service = serviceBuilder.addOperation(SYNTHETIC_OPERATION_ID).build();
104-
ModelTransformer transformer = ModelTransformer.create();
105-
return transformer.replaceShapes(model, Set.of(inputBuilder.build(), operationBuilder.build(), service));
106-
}
10750
}

0 commit comments

Comments
 (0)