Skip to content

Duplicate all eventstream event shapes + add new legacy event modes #6052

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static CodegenCustomizationProcessor getProcessorFor(
new SmithyRpcV2CborProtocolProcessor(),
new RemoveExceptionMessagePropertyProcessor(),
new UseLegacyEventGenerationSchemeProcessor(),
new EventStreamUniqueEventShapesProcessor(config.getUseLegacyEventGenerationScheme()),
new NewAndLegacyEventStreamProcessor(),
new S3RemoveBucketFromUriProcessor(),
new S3ControlRemoveAccountIdHostPrefixProcessor(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.codegen.customization.processors;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import software.amazon.awssdk.codegen.customization.CodegenCustomizationProcessor;
import software.amazon.awssdk.codegen.model.config.customization.LegacyEventGenerationMode;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.model.service.Shape;
import software.amazon.awssdk.utils.Logger;

/**
* Processor for eventstreams that ensures that all eventstream event shapes are unique - for each eventstream/event it creates a
* new shape with a unique name constructed from the EventStream and Event shape names: `[ShapeName][EventStreamName]`. Any legacy
* eventstream/events (configured with the useLegacyEventGenerationScheme customization) are skipped. When an event shape is
* shared between multiple eventstreams, it causes SDK generation/compilation failures. The top level shape POJO implements the
* event stream interface for each stream and the return type of the sdkEventType method conflicts.
*/
public final class EventStreamUniqueEventShapesProcessor implements CodegenCustomizationProcessor {
private static final Logger log = Logger.loggerFor(EventStreamUniqueEventShapesProcessor.class);

private final Map<String, Map<String, LegacyEventGenerationMode>> useLegacyEventGenerationScheme;

public EventStreamUniqueEventShapesProcessor(
Map<String, Map<String, LegacyEventGenerationMode>> useLegacyEventGenerationScheme) {
this.useLegacyEventGenerationScheme = useLegacyEventGenerationScheme;
}

@Override
public void preprocess(ServiceModel serviceModel) {
Map<String, Shape> newEventShapes = new HashMap<>();
serviceModel.getShapes().forEach((name, shape) -> {
if (!shape.isEventstream()) {
return;
}

preprocessEventStream(serviceModel, name, shape, newEventShapes);
});
serviceModel.getShapes().putAll(newEventShapes);
}

private void preprocessEventStream(ServiceModel serviceModel, String eventStreamName, Shape eventStreamShape, Map<String,
Shape> newEventShapes) {
Map<String, LegacyEventGenerationMode> eventLegacyModes = useLegacyEventGenerationScheme
.getOrDefault(eventStreamName, Collections.emptyMap());

eventStreamShape.getMembers().forEach((memberName, member) -> {
String eventShapeName = member.getShape();
Shape memberTargetShape = serviceModel.getShape(eventShapeName);
LegacyEventGenerationMode legacyEventGenerationMode = eventLegacyModes
.getOrDefault(memberName, LegacyEventGenerationMode.DISABLED);

if (memberTargetShape.isEvent() && legacyEventGenerationMode == LegacyEventGenerationMode.DISABLED) {
String newShapeName = eventShapeName + eventStreamName;
if (serviceModel.getShapes().containsKey(newShapeName)) {
// TODO: This could be an error instead. Its unlikely we'll run into this. And if we do, not creating the
// unique name is only an issue when/if the event is shared with another event stream whos event/eventstream
// shape name is also in the model.
log.warn(() -> String.format("Shape name conflict, unable to create a new unique event shape name for %s in"
+ " eventstream %s because %s already exists in the model. Skipping.",
eventShapeName, eventStreamName, newShapeName));
} else {
log.debug(() -> String.format("Creating new, unique, event shape for %s in eventstream %s: %s",
eventShapeName, eventStreamName, newShapeName));
newEventShapes.put(newShapeName, memberTargetShape);
member.setShape(newShapeName);
}
}
});
}

@Override
public void postprocess(IntermediateModel intermediateModel) {
// no-op
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
package software.amazon.awssdk.codegen.customization.processors;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.codegen.customization.CodegenCustomizationProcessor;
import software.amazon.awssdk.codegen.model.config.customization.LegacyEventGenerationMode;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
Expand All @@ -42,9 +42,8 @@ public void preprocess(ServiceModel serviceModel) {

@Override
public void postprocess(IntermediateModel intermediateModel) {
Map<String, List<String>> useLegacyEventGenerationScheme = intermediateModel.getCustomizationConfig()
.getUseLegacyEventGenerationScheme();

Map<String, Map<String, LegacyEventGenerationMode>> useLegacyEventGenerationScheme =
intermediateModel.getCustomizationConfig().getUseLegacyEventGenerationScheme();
useLegacyEventGenerationScheme.forEach((eventStream, members) -> {
ShapeModel shapeModel = getShapeByC2jName(intermediateModel, eventStream);

Expand All @@ -56,13 +55,15 @@ public void postprocess(IntermediateModel intermediateModel) {

Map<String, Integer> shapeToEventCount = new HashMap<>();

members.forEach(m -> {
members.forEach((m, legacyEventGenerationMode) -> {
MemberModel event = shapeModel.getMemberByC2jName(m);

if (event != null) {
String shapeName = event.getC2jShape();
int count = shapeToEventCount.getOrDefault(shapeName, 0);
shapeToEventCount.put(shapeName, ++count);
if (legacyEventGenerationMode == LegacyEventGenerationMode.NO_EVENT_SUBCLASS) {
String shapeName = event.getC2jShape();
int count = shapeToEventCount.getOrDefault(shapeName, 0);
shapeToEventCount.put(shapeName, ++count);
}
} else {
String msg = String.format("Encountered %s customization for unrecognized eventstream member %s#%s",
CUSTOMIZATION_NAME, eventStream, m);
Expand All @@ -73,7 +74,7 @@ public void postprocess(IntermediateModel intermediateModel) {
shapeToEventCount.forEach((shape, count) -> {
if (count > 1) {
throw new IllegalArgumentException(CUSTOMIZATION_NAME + " customization declared for "
+ eventStream + ", but more than it targets more than one member with the shape " + shape);
+ eventStream + ", but it targets more than one member with the shape " + shape);
}
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public class CustomizationConfig {
* generation scheme for the visitor methods was changed. There should be no good reason to use this customization
* for any other purpose.
*/
private Map<String, List<String>> useLegacyEventGenerationScheme = new HashMap<>();
private Map<String, Map<String, LegacyEventGenerationMode>> useLegacyEventGenerationScheme = new HashMap<>();

/**
* How the code generator should behave when it encounters shapes with underscores in the name.
Expand Down Expand Up @@ -646,11 +646,12 @@ public void setAllowEndpointOverrideForEndpointDiscoveryRequiredOperations(
allowEndpointOverrideForEndpointDiscoveryRequiredOperations;
}

public Map<String, List<String>> getUseLegacyEventGenerationScheme() {
public Map<String, Map<String, LegacyEventGenerationMode>> getUseLegacyEventGenerationScheme() {
return useLegacyEventGenerationScheme;
}

public void setUseLegacyEventGenerationScheme(Map<String, List<String>> useLegacyEventGenerationScheme) {
public void setUseLegacyEventGenerationScheme(
Map<String, Map<String, LegacyEventGenerationMode>> useLegacyEventGenerationScheme) {
this.useLegacyEventGenerationScheme = useLegacyEventGenerationScheme;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.codegen.model.config.customization;

/**
* Legacy event generation modes.
*/
public enum LegacyEventGenerationMode {
DISABLED,
NO_EVENT_SUBCLASS, // old legacy - do not generate subclasses of events
NO_UNIQUE_EVENT_NAMES // new legacy - do not duplicate event shapes to ensure unique events
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeSpec;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.lang.model.element.Modifier;
import software.amazon.awssdk.codegen.model.config.customization.LegacyEventGenerationMode;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
Expand Down Expand Up @@ -56,16 +56,16 @@ public String eventPackageName() {
}

public boolean useLegacyGenerationScheme(MemberModel event) {
Map<String, List<String>> useLegacyEventGenerationScheme = intermediateModel.getCustomizationConfig()
.getUseLegacyEventGenerationScheme();
Map<String, Map<String, LegacyEventGenerationMode>> useLegacyEventGenerationScheme =
intermediateModel.getCustomizationConfig().getUseLegacyEventGenerationScheme();

List<String> targetEvents = useLegacyEventGenerationScheme.get(eventStream.getC2jName());
Map<String, LegacyEventGenerationMode> targetEvents = useLegacyEventGenerationScheme.get(eventStream.getC2jName());

if (targetEvents == null) {
if (targetEvents == null || !targetEvents.containsKey(event.getC2jName())) {
return false;
}

return targetEvents.stream().anyMatch(e -> e.equals(event.getC2jName()));
return targetEvents.get(event.getC2jName()).equals(LegacyEventGenerationMode.NO_EVENT_SUBCLASS);
}

public ClassName eventClassName(MemberModel eventModel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


import java.io.File;
import org.junit.BeforeClass;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
Expand All @@ -39,11 +39,11 @@ public class UseLegacyEventGenerationSchemeProcessorTest {
@Rule
public ExpectedException thrown = ExpectedException.none();

private static ServiceModel serviceModel;
private ServiceModel serviceModel;


@BeforeClass
public static void setup() {
@Before
public void setup() {
String c2jFilePath = UseLegacyEventGenerationSchemeProcessorTest.class.getResource(RESOURCE_ROOT + "/service-2.json").getFile();
File c2jFile = new File(c2jFilePath);

Expand All @@ -67,7 +67,7 @@ public void testPostProcess_customizationIsValid_succeeds() {
}


private static IntermediateModel intermediateModelWithConfig(String configName) {
private IntermediateModel intermediateModelWithConfig(String configName) {
return new IntermediateModelBuilder(C2jModels.builder()
.serviceModel(serviceModel)
.customizationConfig(loadCustomizationConfig(configName))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"useLegacyEventGenerationScheme": {
"EventStream": ["EventOne"]
"EventStream": {"EventOne": "NO_EVENT_SUBCLASS"}
},
"underscoresInNameBehavior": "ALLOW"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"useLegacyEventGenerationScheme": {
"EventStream": ["EventOne", "secondEventOne"]
}
"EventStream": {"EventOne": "NO_EVENT_SUBCLASS", "secondEventOne": "NO_EVENT_SUBCLASS"}
},
"underscoresInNameBehavior": "ALLOW"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,17 @@
"createMethodParams": ["param1", "param2", "param3"]
},
"useLegacyEventGenerationScheme": {
"EventStream": ["EventOne", "event-two", "eventThree"]
"InputEventStream": {"InputEvent": "NO_UNIQUE_EVENT_NAMES"},
"InputEventStreamTwo": {
"InputEventOne": "NO_UNIQUE_EVENT_NAMES",
"InputEventTwo": "NO_UNIQUE_EVENT_NAMES"
},
"EventStream": {
"EventOne": "NO_EVENT_SUBCLASS",
"eventThree": "NO_EVENT_SUBCLASS",
"EventTheSecond": "NO_UNIQUE_EVENT_NAMES",
"secondEventOne": "NO_UNIQUE_EVENT_NAMES"
}
},
"customServiceMetadata": {
"protocol": "cbor"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@
"returnType": "software.amazon.awssdk.services.json.JsonUtilities",
"createMethodParams": ["param1", "param2", "param3"]
},
"useLegacyEventGenerationScheme": {
"EventStream": ["EventOne", "event-two", "eventThree"]
}
"useLegacyEventGenerationScheme": {
"InputEventStream": {"InputEvent": "NO_UNIQUE_EVENT_NAMES"},
"InputEventStreamTwo": {
"InputEventOne": "NO_UNIQUE_EVENT_NAMES",
"InputEventTwo": "NO_UNIQUE_EVENT_NAMES"
},
"EventStream": {
"EventOne": "NO_EVENT_SUBCLASS",
"eventThree": "NO_EVENT_SUBCLASS",
"EventTheSecond": "NO_UNIQUE_EVENT_NAMES",
"secondEventOne": "NO_UNIQUE_EVENT_NAMES"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,17 @@
"software.amazon.awssdk.codegen.poet.plugins.InternalTestPlugin2"
],
"useLegacyEventGenerationScheme": {
"EventStream": ["EventOne", "event-two", "eventThree"]
"InputEventStream": {"InputEvent": "NO_UNIQUE_EVENT_NAMES"},
"InputEventStreamTwo": {
"InputEventOne": "NO_UNIQUE_EVENT_NAMES",
"InputEventTwo": "NO_UNIQUE_EVENT_NAMES"
},
"EventStream": {
"EventOne": "NO_EVENT_SUBCLASS",
"eventThree": "NO_EVENT_SUBCLASS",
"EventTheSecond": "NO_UNIQUE_EVENT_NAMES",
"secondEventOne": "NO_UNIQUE_EVENT_NAMES"
}
},
"asyncClientDecoratorClass": true,
"syncClientDecoratorClass": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
{
"useLegacyEventGenerationScheme": {
"InputEventStream": {"InputEvent": "NO_UNIQUE_EVENT_NAMES"},
"InputEventStreamTwo": {
"InputEventOne": "NO_UNIQUE_EVENT_NAMES",
"InputEventTwo": "NO_UNIQUE_EVENT_NAMES"
},
"EventStream": {
"EventOne": "NO_UNIQUE_EVENT_NAMES",
"EventTwo": "NO_UNIQUE_EVENT_NAMES",
"secondEventOne": "NO_UNIQUE_EVENT_NAMES",
"secondeventtwo": "NO_UNIQUE_EVENT_NAMES"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,16 @@
}
},
"underscoresInNameBehavior": "ALLOW",
"requiredTraitValidationEnabled": true
"requiredTraitValidationEnabled": true,
"useLegacyEventGenerationScheme": {
"InputEventStream": {"InputEvent": "NO_UNIQUE_EVENT_NAMES"},
"InputEventStreamTwo": {"InputEventTwo": "NO_UNIQUE_EVENT_NAMES"},
"EventStream": {
"EventOne": "NO_UNIQUE_EVENT_NAMES",
"SecondEventOne": "NO_UNIQUE_EVENT_NAMES",
"EventTwo": "NO_UNIQUE_EVENT_NAMES",
"SecondEventTwo": "NO_UNIQUE_EVENT_NAMES",
"eventthree": "NO_UNIQUE_EVENT_NAMES"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void accept(StreamDeathsResponseHandler.Visitor visitor) {
/**
* Create a builder for the {@code Person} event type for this stream.
*/
static Person.Builder personBuilder() {
static PersonEventStream.Builder personBuilder() {
return DefaultPerson.builder();
}

Expand Down
Loading
Loading