Skip to content

Commit ad6544c

Browse files
committed
Include event type in visitor name
This commit fixes event visitor method generation by including the *event type* in the visitor name. Note that event type here is actually the member name in the encompassing eventstream structure, *not* the Java class of the event. For example, in the example below, "Foo" is the event type. "MyEventStream": { "type": "structure", "eventstream": true, "members": { "Foo": { "shape": "Bar" } } } To ensure that this change is backwards compatible with existing services that have event streams (Transcribe Streaming and Kinesis), we also include a customization to supress this behavior keep the method name as "visit". Note that by sheer luck, the existing consumer method setters, e.g. Builder onSubscribeToShardEvent(Consumer<SubscribeToShardEvent>); are unaffected by this change. The previous generation scheme for these methods is "on{Generation Java class name for event shape}"; now it's "on{event type}". However, both Transcribe and Kinesis use the shape name as the member name (or vise versa?) in their current iteration of their event streams. For example, in Kinesis, SubscribeToShard's output stream is defined as "SubscribeToShardEventStream":{ "type":"structure", "required":["SubscribeToShardEvent"], "members":{ "SubscribeToShardEvent":{"shape":"SubscribeToShardEvent"}, ... }, "eventstream":true }
1 parent 9df1462 commit ad6544c

File tree

19 files changed

+849
-33
lines changed

19 files changed

+849
-33
lines changed

codegen/src/main/java/software/amazon/awssdk/codegen/customization/processors/DefaultCustomizationProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public static CodegenCustomizationProcessor getProcessorFor(
3333
new ShapeModifiersProcessor(config.getShapeModifiers()),
3434
new ShapeSubstitutionsProcessor(config.getShapeSubstitutions()),
3535
new OperationModifiersProcessor(config.getOperationModifiers()),
36-
new RemoveExceptionMessagePropertyProcessor()
36+
new RemoveExceptionMessagePropertyProcessor(),
37+
new ExcludeEventNameFromVisitMethodProcessor()
3738
);
3839
}
3940
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.customization.processors;
17+
18+
import java.util.HashMap;
19+
import java.util.List;
20+
import java.util.Map;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import software.amazon.awssdk.codegen.customization.CodegenCustomizationProcessor;
24+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
25+
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
26+
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
27+
import software.amazon.awssdk.codegen.model.service.ServiceModel;
28+
29+
/**
30+
* This process enforces constraints placed on the "excludeEventNameFromVisitMethod"; i.e. that no two members
31+
* of the same event stream sharing the same shape have this customization enabled for them. This processor does not
32+
* modify the the service or intermediate model.
33+
*/
34+
public class ExcludeEventNameFromVisitMethodProcessor implements CodegenCustomizationProcessor {
35+
private static final String CUSTOMIZATION_NAME = "ExcludeEventNameFromVisitMethod";
36+
private static final Logger log = LoggerFactory.getLogger(ExcludeEventNameFromVisitMethodProcessor.class);
37+
38+
@Override
39+
public void preprocess(ServiceModel serviceModel) {
40+
// no-op
41+
}
42+
43+
@Override
44+
public void postprocess(IntermediateModel intermediateModel) {
45+
Map<String, List<String>> excludeEventNameFromVisitMethod = intermediateModel.getCustomizationConfig()
46+
.getExcludeEventNameFromVisitMethod();
47+
48+
excludeEventNameFromVisitMethod.forEach((eventStream, members) -> {
49+
ShapeModel shapeModel = getShapeByC2jName(intermediateModel, eventStream);
50+
51+
if (shapeModel == null || !shapeModel.isEventStream()) {
52+
log.warn("Encountered " + CUSTOMIZATION_NAME + " for unrecognized eventstream " + eventStream);
53+
return;
54+
}
55+
56+
Map<String, Integer> shapeToEventCount = new HashMap<>();
57+
58+
members.forEach(m -> {
59+
MemberModel event = shapeModel.getMemberByC2jName(m);
60+
61+
if (event != null) {
62+
String shapeName = event.getC2jShape();
63+
int count = shapeToEventCount.getOrDefault(shapeName, 0);
64+
shapeToEventCount.put(shapeName, ++count);
65+
} else {
66+
String msg = String.format("Encountered %s customization for unrecognized eventstream member %s#%s",
67+
CUSTOMIZATION_NAME, eventStream, m);
68+
log.warn(msg);
69+
}
70+
});
71+
72+
shapeToEventCount.forEach((shape, count) -> {
73+
if (count > 1) {
74+
throw new IllegalArgumentException(CUSTOMIZATION_NAME + " customization declared for "
75+
+ eventStream + ", but more than it targets more than one member with the shape " + shape);
76+
}
77+
});
78+
});
79+
}
80+
81+
private ShapeModel getShapeByC2jName(IntermediateModel intermediateModel, String c2jName) {
82+
return intermediateModel.getShapes().values().stream()
83+
.filter(s -> s.getC2jName().equals(c2jName))
84+
.findAny()
85+
.orElse(null);
86+
}
87+
}

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ public class CustomizationConfig {
176176
*/
177177
private boolean allowEndpointOverrideForEndpointDiscoveryRequiredOperations = false;
178178

179+
/**
180+
* Customization to instruct the code generator *not* to include the event's name in the {@code Visitor#visit*()}
181+
* method; i.e. the signature of the method will be {@code void visit(<Event Shape type>)}.
182+
* <p>
183+
* <b>NOTE</b>This customization is primarily here to preserve backwards compatibility with existing code before the
184+
* generation scheme for the visitor methods was changed. There should be no good reason to use this customization
185+
* for any other purpose.
186+
*/
187+
private Map<String, List<String>> excludeEventNameFromVisitMethod = new HashMap<>();
188+
179189
private CustomizationConfig() {
180190
}
181191

@@ -454,4 +464,12 @@ public void setAllowEndpointOverrideForEndpointDiscoveryRequiredOperations(
454464
this.allowEndpointOverrideForEndpointDiscoveryRequiredOperations =
455465
allowEndpointOverrideForEndpointDiscoveryRequiredOperations;
456466
}
467+
468+
public Map<String, List<String>> getExcludeEventNameFromVisitMethod() {
469+
return excludeEventNameFromVisitMethod;
470+
}
471+
472+
public void setExcludeEventNameFromVisitMethod(Map<String, List<String>> excludeEventNameFromVisitMethod) {
473+
this.excludeEventNameFromVisitMethod = excludeEventNameFromVisitMethod;
474+
}
457475
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/eventstream/EventStreamResponseHandlerSpec.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import software.amazon.awssdk.annotations.SdkPublicApi;
2424
import software.amazon.awssdk.awscore.eventstream.EventStreamResponseHandler;
2525
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
26+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
2627
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
2728
import software.amazon.awssdk.codegen.poet.ClassSpec;
2829
import software.amazon.awssdk.codegen.poet.PoetExtensions;
@@ -33,6 +34,7 @@
3334
*/
3435
public class EventStreamResponseHandlerSpec implements ClassSpec {
3536

37+
private final IntermediateModel intermediateModel;
3638
private final PoetExtensions poetExt;
3739
private final OperationModel operationModel;
3840
private final String apiName;
@@ -41,6 +43,7 @@ public class EventStreamResponseHandlerSpec implements ClassSpec {
4143
private final ClassName eventStreamBaseClass;
4244

4345
public EventStreamResponseHandlerSpec(GeneratorTaskParams params, OperationModel operationModel) {
46+
this.intermediateModel = params.getModel();
4447
this.poetExt = params.getPoetExtensions();
4548
this.operationModel = operationModel;
4649
this.apiName = poetExt.getApiName(operationModel);
@@ -61,7 +64,7 @@ public TypeSpec poetSpec() {
6164
.addJavadoc("Response handler for the $L API.", apiName)
6265
.addMethod(builderMethodSpec())
6366
.addType(new EventStreamResponseHandlerBuilderInterfaceSpec(poetExt, operationModel).poetSpec())
64-
.addType(new EventStreamVisitorInterfaceSpec(poetExt, operationModel).poetSpec())
67+
.addType(new EventStreamVisitorInterfaceSpec(intermediateModel, poetExt, operationModel).poetSpec())
6568
.build();
6669
}
6770

codegen/src/main/java/software/amazon/awssdk/codegen/poet/eventstream/EventStreamUtils.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ public static Optional<ShapeModel> getBaseEventStreamShape(IntermediateModel mod
9696
}
9797

9898
/**
99-
* Returns the stream of event member shapes ('event: true') excluding exceptions
99+
* Returns the stream of event members ('event: true') excluding exceptions
100100
* from the input event stream shape ('eventstream: true').
101101
*/
102-
public static Stream<ShapeModel> getEvents(ShapeModel eventStreamShape) {
103-
return getEventMembers(eventStreamShape).map(MemberModel::getShape);
102+
public static Stream<MemberModel> getEvents(ShapeModel eventStreamShape) {
103+
return getEventMembers(eventStreamShape);
104104
}
105105

106106
/**

codegen/src/main/java/software/amazon/awssdk/codegen/poet/eventstream/EventStreamVisitorBuilderImplSpec.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@
2525
import javax.lang.model.element.Modifier;
2626
import software.amazon.awssdk.annotations.SdkInternalApi;
2727
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
28+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
29+
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
2830
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
29-
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
3031
import software.amazon.awssdk.codegen.poet.PoetExtensions;
3132
import software.amazon.awssdk.codegen.poet.PoetUtils;
3233

3334
/**
3435
* Generates the implementation for the builder of an event stream visitor.
3536
*/
3637
public class EventStreamVisitorBuilderImplSpec extends EventStreamVisitorBuilderInterfaceSpec {
37-
38+
private final IntermediateModel intermediateModel;
3839
private final PoetExtensions poetExt;
3940
private final OperationModel opModel;
4041
private final ClassName visitorType;
@@ -43,6 +44,7 @@ public class EventStreamVisitorBuilderImplSpec extends EventStreamVisitorBuilder
4344

4445
public EventStreamVisitorBuilderImplSpec(GeneratorTaskParams params, OperationModel operationModel) {
4546
super(params.getPoetExtensions(), operationModel);
47+
this.intermediateModel = params.getModel();
4648
this.poetExt = params.getPoetExtensions();
4749
this.opModel = operationModel;
4850
this.visitorType = poetExt.eventStreamResponseHandlerVisitorType(opModel);
@@ -68,7 +70,7 @@ private class VisitorFromBuilderImplSpec extends EventStreamVisitorInterfaceSpec
6870
private final MethodSpec.Builder constrBuilder;
6971

7072
VisitorFromBuilderImplSpec() {
71-
super(poetExt, opModel);
73+
super(intermediateModel, poetExt, opModel);
7274
this.constrBuilder = MethodSpec.constructorBuilder()
7375
.addParameter(enclosingClassName(), "builder")
7476
.addStatement("this.onDefault = builder.onDefault != null ?\n"
@@ -98,10 +100,10 @@ protected MethodSpec.Builder applyVisitDefaultMethodSpecUpdates(MethodSpec.Build
98100
@Override
99101
protected MethodSpec.Builder applyVisitSubTypeMethodSpecUpdates(TypeSpec.Builder typeBuilder,
100102
MethodSpec.Builder methodBuilder,
101-
ShapeModel s) {
102-
ClassName eventSubType = poetExt.getModelClass(s.getShapeName());
103+
MemberModel event) {
104+
ClassName eventSubType = poetExt.getModelClass(event.getShape().getShapeName());
103105
TypeName eventConsumerType = consumerType(eventSubType);
104-
FieldSpec consumerField = FieldSpec.builder(eventConsumerType, "on" + eventSubType.simpleName())
106+
FieldSpec consumerField = FieldSpec.builder(eventConsumerType, eventConsumerName(event))
105107
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
106108
.build();
107109
typeBuilder.addField(consumerField);
@@ -127,10 +129,10 @@ private ClassName enclosingClassName() {
127129
@Override
128130
protected MethodSpec.Builder applyOnSubTypeMethodSpecUpdates(TypeSpec.Builder typeBuilder,
129131
MethodSpec.Builder methodBuilder,
130-
ShapeModel eventSubTypeShape) {
131-
ClassName eventSubType = poetExt.getModelClass(eventSubTypeShape.getShapeName());
132+
MemberModel event) {
133+
ClassName eventSubType = poetExt.getModelClass(event.getShape().getShapeName());
132134
TypeName eventConsumerType = consumerType(eventSubType);
133-
FieldSpec consumerField = FieldSpec.builder(eventConsumerType, "on" + eventSubType.simpleName())
135+
FieldSpec consumerField = FieldSpec.builder(eventConsumerType, eventConsumerName(event))
134136
.addModifiers(Modifier.PRIVATE)
135137
.build();
136138
typeBuilder.addField(consumerField);

codegen/src/main/java/software/amazon/awssdk/codegen/poet/eventstream/EventStreamVisitorBuilderInterfaceSpec.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
import java.util.function.Consumer;
2424
import javax.lang.model.element.Modifier;
2525
import software.amazon.awssdk.codegen.docs.DocumentationBuilder;
26+
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
2627
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
2728
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
2829
import software.amazon.awssdk.codegen.poet.ClassSpec;
2930
import software.amazon.awssdk.codegen.poet.PoetExtensions;
3031
import software.amazon.awssdk.codegen.poet.PoetUtils;
32+
import software.amazon.awssdk.utils.internal.CodegenNamingUtils;
3133

3234
/**
3335
* Spec for builder interface for visitor.
@@ -52,8 +54,8 @@ public final TypeSpec poetSpec() {
5254
.addMethod(applyBuildMethodSpecUpdates(createBuildMethodSpec()).build());
5355

5456
EventStreamUtils.getEvents(eventStreamShape)
55-
.forEach(s -> typeBuilder.addMethod(
56-
applyOnSubTypeMethodSpecUpdates(typeBuilder, createOnSubTypeMethodSpec(s), s)
57+
.forEach(e -> typeBuilder.addMethod(
58+
applyOnSubTypeMethodSpecUpdates(typeBuilder, createOnSubTypeMethodSpec(e), e)
5759
.build()));
5860

5961
return typeBuilder.build();
@@ -105,8 +107,8 @@ protected MethodSpec.Builder applyBuildMethodSpecUpdates(MethodSpec.Builder buil
105107

106108
protected MethodSpec.Builder applyOnSubTypeMethodSpecUpdates(TypeSpec.Builder typeBuilder,
107109
MethodSpec.Builder methodBuilder,
108-
ShapeModel eventSubTypeShape) {
109-
ClassName eventSubType = poetExt.getModelClass(eventSubTypeShape.getShapeName());
110+
MemberModel event) {
111+
ClassName eventSubType = poetExt.getModelClass(event.getShape().getShapeName());
110112
String javadocs = new DocumentationBuilder()
111113
.description("Callback to invoke when a {@link $T} is visited.")
112114
.param("c", "Callback to process the event.")
@@ -116,13 +118,16 @@ protected MethodSpec.Builder applyOnSubTypeMethodSpecUpdates(TypeSpec.Builder ty
116118
.addJavadoc(javadocs, eventSubType);
117119
}
118120

119-
private MethodSpec.Builder createOnSubTypeMethodSpec(ShapeModel eventSubTypeShape) {
120-
ClassName eventSubType = poetExt.getModelClass(eventSubTypeShape.getShapeName());
121+
private MethodSpec.Builder createOnSubTypeMethodSpec(MemberModel event) {
122+
ClassName eventSubType = poetExt.getModelClass(event.getShape().getShapeName());
121123
ParameterizedTypeName eventConsumerType = ParameterizedTypeName.get(ClassName.get(Consumer.class), eventSubType);
122-
return MethodSpec.methodBuilder("on" + eventSubType.simpleName())
124+
return MethodSpec.methodBuilder(eventConsumerName(event))
123125
.addModifiers(Modifier.PUBLIC)
124126
.addParameter(ParameterSpec.builder(eventConsumerType, "c").build())
125127
.returns(visitorBuilderType);
126128
}
127129

130+
protected final String eventConsumerName(MemberModel event) {
131+
return "on" + CodegenNamingUtils.pascalCase(event.getName());
132+
}
128133
}

0 commit comments

Comments
 (0)