diff --git a/.changes/next-release/feature-AWSSDKforJavav2-f004fae.json b/.changes/next-release/feature-AWSSDKforJavav2-f004fae.json
new file mode 100644
index 000000000000..184769e3f85c
--- /dev/null
+++ b/.changes/next-release/feature-AWSSDKforJavav2-f004fae.json
@@ -0,0 +1,6 @@
+{
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Add support for validating that shared models between two services are identical."
+}
diff --git a/codegen/pom.xml b/codegen/pom.xml
index bb9c7874c6a8..d539fc07f4d4 100644
--- a/codegen/pom.xml
+++ b/codegen/pom.xml
@@ -239,5 +239,10 @@
mockito-core
compile
+
+ nl.jqno.equalsverifier
+ equalsverifier
+ test
+
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/CodeGenerator.java b/codegen/src/main/java/software/amazon/awssdk/codegen/CodeGenerator.java
index 0bcdbccb4d04..b96b62436ea6 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/CodeGenerator.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/CodeGenerator.java
@@ -32,6 +32,7 @@
import software.amazon.awssdk.codegen.validation.ModelValidationContext;
import software.amazon.awssdk.codegen.validation.ModelValidationReport;
import software.amazon.awssdk.codegen.validation.ModelValidator;
+import software.amazon.awssdk.codegen.validation.SharedModelsValidator;
import software.amazon.awssdk.codegen.validation.ValidationEntry;
import software.amazon.awssdk.utils.Logger;
@@ -39,8 +40,9 @@ public class CodeGenerator {
private static final Logger log = Logger.loggerFor(CodeGenerator.class);
private static final String MODEL_DIR_NAME = "models";
- // TODO: add validators
- private static final List DEFAULT_MODEL_VALIDATORS = Collections.emptyList();
+ private static final List DEFAULT_MODEL_VALIDATORS = Collections.singletonList(
+ new SharedModelsValidator()
+ );
private final C2jModels c2jModels;
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ArgumentModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ArgumentModel.java
index 5013db7d3f9e..16e848303a4f 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ArgumentModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ArgumentModel.java
@@ -15,6 +15,8 @@
package software.amazon.awssdk.codegen.model.intermediate;
+import java.util.Objects;
+
public class ArgumentModel extends DocumentationModel {
private String name;
@@ -61,4 +63,28 @@ public ArgumentModel withIsEnumArg(boolean isEnumArg) {
this.isEnumArg = isEnumArg;
return this;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ ArgumentModel that = (ArgumentModel) o;
+ return isEnumArg == that.isEnumArg
+ && Objects.equals(name, that.name)
+ && Objects.equals(type, that.type);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + Objects.hashCode(name);
+ result = 31 * result + Objects.hashCode(type);
+ result = 31 * result + Boolean.hashCode(isEnumArg);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/AuthorizerModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/AuthorizerModel.java
index ce98c0dfea8e..316f4e741139 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/AuthorizerModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/AuthorizerModel.java
@@ -16,6 +16,7 @@
package software.amazon.awssdk.codegen.model.intermediate;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.Objects;
import software.amazon.awssdk.codegen.model.service.Location;
public class AuthorizerModel extends DocumentationModel {
@@ -63,4 +64,30 @@ public String getAddAuthTokenMethod() {
authTokenLocation));
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ AuthorizerModel that = (AuthorizerModel) o;
+ return Objects.equals(name, that.name)
+ && Objects.equals(interfaceName, that.interfaceName)
+ && authTokenLocation == that.authTokenLocation
+ && Objects.equals(tokenName, that.tokenName);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + Objects.hashCode(name);
+ result = 31 * result + Objects.hashCode(interfaceName);
+ result = 31 * result + Objects.hashCode(authTokenLocation);
+ result = 31 * result + Objects.hashCode(tokenName);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/DocumentationModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/DocumentationModel.java
index 5be891040acc..55fd39f4a7c7 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/DocumentationModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/DocumentationModel.java
@@ -17,6 +17,8 @@
import static software.amazon.awssdk.codegen.internal.DocumentationUtils.escapeIllegalCharacters;
+import java.util.Objects;
+
public class DocumentationModel {
protected String documentation;
@@ -28,4 +30,22 @@ public String getDocumentation() {
public void setDocumentation(String documentation) {
this.documentation = escapeIllegalCharacters(documentation);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DocumentationModel that = (DocumentationModel) o;
+ return Objects.equals(documentation, that.documentation);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(documentation);
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/EndpointDiscovery.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/EndpointDiscovery.java
index 91a5f3b60f25..e372079fc541 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/EndpointDiscovery.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/EndpointDiscovery.java
@@ -26,4 +26,22 @@ public boolean isRequired() {
public void setRequired(boolean required) {
this.required = required;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ EndpointDiscovery that = (EndpointDiscovery) o;
+ return required == that.required;
+ }
+
+ @Override
+ public int hashCode() {
+ return Boolean.hashCode(required);
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/EnumModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/EnumModel.java
index f469b5de99fd..652f2c2aca6e 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/EnumModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/EnumModel.java
@@ -15,6 +15,8 @@
package software.amazon.awssdk.codegen.model.intermediate;
+import java.util.Objects;
+
/**
* Represents a single enum field in a enum.
*/
@@ -49,4 +51,23 @@ public String getValue() {
return value;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ EnumModel enumModel = (EnumModel) o;
+ return Objects.equals(value, enumModel.value) && Objects.equals(name, enumModel.name);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(value);
+ result = 31 * result + Objects.hashCode(name);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/MemberModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/MemberModel.java
index fddf93d4d72d..3e905aa1ed56 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/MemberModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/MemberModel.java
@@ -28,6 +28,7 @@
import com.squareup.javapoet.ClassName;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import software.amazon.awssdk.codegen.internal.TypeUtils;
import software.amazon.awssdk.codegen.model.service.ContextParam;
@@ -785,4 +786,98 @@ public void ignoreDataTypeConversionFailures(boolean ignoreDataTypeConversionFai
public boolean ignoreDataTypeConversionFailures() {
return ignoreDataTypeConversionFailures;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ MemberModel that = (MemberModel) o;
+ return deprecated == that.deprecated
+ && required == that.required
+ && synthetic == that.synthetic
+ && idempotencyToken == that.idempotencyToken
+ && isJsonValue == that.isJsonValue
+ && eventPayload == that.eventPayload
+ && eventHeader == that.eventHeader
+ && endpointDiscoveryId == that.endpointDiscoveryId
+ && sensitive == that.sensitive
+ && xmlAttribute == that.xmlAttribute
+ && ignoreDataTypeConversionFailures == that.ignoreDataTypeConversionFailures
+ && Objects.equals(name, that.name)
+ && Objects.equals(c2jName, that.c2jName)
+ && Objects.equals(c2jShape, that.c2jShape)
+ && Objects.equals(variable, that.variable)
+ && Objects.equals(setterModel, that.setterModel)
+ && Objects.equals(getterModel, that.getterModel)
+ && Objects.equals(http, that.http)
+ && Objects.equals(deprecatedMessage, that.deprecatedMessage)
+ && Objects.equals(listModel, that.listModel)
+ && Objects.equals(mapModel, that.mapModel)
+ && Objects.equals(enumType, that.enumType)
+ && Objects.equals(xmlNameSpaceUri, that.xmlNameSpaceUri)
+ && Objects.equals(shape, that.shape)
+ && Objects.equals(fluentGetterMethodName, that.fluentGetterMethodName)
+ && Objects.equals(fluentEnumGetterMethodName, that.fluentEnumGetterMethodName)
+ && Objects.equals(fluentSetterMethodName, that.fluentSetterMethodName)
+ && Objects.equals(fluentEnumSetterMethodName, that.fluentEnumSetterMethodName)
+ && Objects.equals(existenceCheckMethodName, that.existenceCheckMethodName)
+ && Objects.equals(beanStyleGetterName, that.beanStyleGetterName)
+ && Objects.equals(beanStyleSetterName, that.beanStyleSetterName)
+ && Objects.equals(unionEnumTypeName, that.unionEnumTypeName)
+ && Objects.equals(timestampFormat, that.timestampFormat)
+ && Objects.equals(deprecatedName, that.deprecatedName)
+ && Objects.equals(fluentDeprecatedGetterMethodName, that.fluentDeprecatedGetterMethodName)
+ && Objects.equals(fluentDeprecatedSetterMethodName, that.fluentDeprecatedSetterMethodName)
+ && Objects.equals(deprecatedBeanStyleSetterMethodName, that.deprecatedBeanStyleSetterMethodName)
+ && Objects.equals(contextParam, that.contextParam);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + Objects.hashCode(name);
+ result = 31 * result + Objects.hashCode(c2jName);
+ result = 31 * result + Objects.hashCode(c2jShape);
+ result = 31 * result + Objects.hashCode(variable);
+ result = 31 * result + Objects.hashCode(setterModel);
+ result = 31 * result + Objects.hashCode(getterModel);
+ result = 31 * result + Objects.hashCode(http);
+ result = 31 * result + Boolean.hashCode(deprecated);
+ result = 31 * result + Objects.hashCode(deprecatedMessage);
+ result = 31 * result + Boolean.hashCode(required);
+ result = 31 * result + Boolean.hashCode(synthetic);
+ result = 31 * result + Objects.hashCode(listModel);
+ result = 31 * result + Objects.hashCode(mapModel);
+ result = 31 * result + Objects.hashCode(enumType);
+ result = 31 * result + Objects.hashCode(xmlNameSpaceUri);
+ result = 31 * result + Boolean.hashCode(idempotencyToken);
+ result = 31 * result + Objects.hashCode(shape);
+ result = 31 * result + Objects.hashCode(fluentGetterMethodName);
+ result = 31 * result + Objects.hashCode(fluentEnumGetterMethodName);
+ result = 31 * result + Objects.hashCode(fluentSetterMethodName);
+ result = 31 * result + Objects.hashCode(fluentEnumSetterMethodName);
+ result = 31 * result + Objects.hashCode(existenceCheckMethodName);
+ result = 31 * result + Objects.hashCode(beanStyleGetterName);
+ result = 31 * result + Objects.hashCode(beanStyleSetterName);
+ result = 31 * result + Objects.hashCode(unionEnumTypeName);
+ result = 31 * result + Boolean.hashCode(isJsonValue);
+ result = 31 * result + Objects.hashCode(timestampFormat);
+ result = 31 * result + Boolean.hashCode(eventPayload);
+ result = 31 * result + Boolean.hashCode(eventHeader);
+ result = 31 * result + Boolean.hashCode(endpointDiscoveryId);
+ result = 31 * result + Boolean.hashCode(sensitive);
+ result = 31 * result + Boolean.hashCode(xmlAttribute);
+ result = 31 * result + Objects.hashCode(deprecatedName);
+ result = 31 * result + Objects.hashCode(fluentDeprecatedGetterMethodName);
+ result = 31 * result + Objects.hashCode(fluentDeprecatedSetterMethodName);
+ result = 31 * result + Objects.hashCode(deprecatedBeanStyleSetterMethodName);
+ result = 31 * result + Objects.hashCode(contextParam);
+ result = 31 * result + Boolean.hashCode(ignoreDataTypeConversionFailures);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/OperationModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/OperationModel.java
index a2a060c7a915..6b192644da1d 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/OperationModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/OperationModel.java
@@ -19,6 +19,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import software.amazon.awssdk.codegen.checksum.HttpChecksum;
import software.amazon.awssdk.codegen.compression.RequestCompression;
import software.amazon.awssdk.codegen.docs.ClientType;
@@ -379,4 +380,63 @@ public boolean isUnsignedPayload() {
public void setUnsignedPayload(boolean unsignedPayload) {
this.unsignedPayload = unsignedPayload;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ OperationModel that = (OperationModel) o;
+ return deprecated == that.deprecated && hasBlobMemberAsPayload == that.hasBlobMemberAsPayload
+ && hasStringMemberAsPayload == that.hasStringMemberAsPayload && isAuthenticated == that.isAuthenticated
+ && isPaginated == that.isPaginated && endpointOperation == that.endpointOperation
+ && endpointCacheRequired == that.endpointCacheRequired && httpChecksumRequired == that.httpChecksumRequired
+ && unsignedPayload == that.unsignedPayload && Objects.equals(operationName, that.operationName)
+ && Objects.equals(serviceProtocol, that.serviceProtocol)
+ && Objects.equals(deprecatedMessage, that.deprecatedMessage) && Objects.equals(input, that.input)
+ && Objects.equals(returnType, that.returnType) && Objects.equals(exceptions, that.exceptions)
+ && Objects.equals(simpleMethods, that.simpleMethods) && authType == that.authType
+ && Objects.equals(auth, that.auth) && Objects.equals(endpointDiscovery, that.endpointDiscovery)
+ && Objects.equals(inputShape, that.inputShape) && Objects.equals(outputShape, that.outputShape)
+ && Objects.equals(endpointTrait, that.endpointTrait) && Objects.equals(httpChecksum, that.httpChecksum)
+ && Objects.equals(requestcompression, that.requestcompression)
+ && Objects.equals(staticContextParams, that.staticContextParams)
+ && Objects.equals(operationContextParams, that.operationContextParams);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + Objects.hashCode(operationName);
+ result = 31 * result + Objects.hashCode(serviceProtocol);
+ result = 31 * result + Boolean.hashCode(deprecated);
+ result = 31 * result + Objects.hashCode(deprecatedMessage);
+ result = 31 * result + Objects.hashCode(input);
+ result = 31 * result + Objects.hashCode(returnType);
+ result = 31 * result + Objects.hashCode(exceptions);
+ result = 31 * result + Objects.hashCode(simpleMethods);
+ result = 31 * result + Boolean.hashCode(hasBlobMemberAsPayload);
+ result = 31 * result + Boolean.hashCode(hasStringMemberAsPayload);
+ result = 31 * result + Boolean.hashCode(isAuthenticated);
+ result = 31 * result + Objects.hashCode(authType);
+ result = 31 * result + Objects.hashCode(auth);
+ result = 31 * result + Boolean.hashCode(isPaginated);
+ result = 31 * result + Boolean.hashCode(endpointOperation);
+ result = 31 * result + Boolean.hashCode(endpointCacheRequired);
+ result = 31 * result + Objects.hashCode(endpointDiscovery);
+ result = 31 * result + Objects.hashCode(inputShape);
+ result = 31 * result + Objects.hashCode(outputShape);
+ result = 31 * result + Objects.hashCode(endpointTrait);
+ result = 31 * result + Boolean.hashCode(httpChecksumRequired);
+ result = 31 * result + Objects.hashCode(httpChecksum);
+ result = 31 * result + Objects.hashCode(requestcompression);
+ result = 31 * result + Objects.hashCode(staticContextParams);
+ result = 31 * result + Objects.hashCode(operationContextParams);
+ result = 31 * result + Boolean.hashCode(unsignedPayload);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ParameterHttpMapping.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ParameterHttpMapping.java
index 22ed4a8e6880..fc9a776059a7 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ParameterHttpMapping.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ParameterHttpMapping.java
@@ -15,6 +15,7 @@
package software.amazon.awssdk.codegen.model.intermediate;
+import java.util.Objects;
import software.amazon.awssdk.codegen.model.service.Location;
import software.amazon.awssdk.core.protocol.MarshallLocation;
@@ -199,4 +200,40 @@ public MarshallLocation getMarshallLocation() {
}
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ParameterHttpMapping that = (ParameterHttpMapping) o;
+ return isPayload == that.isPayload
+ && isStreaming == that.isStreaming
+ && flattened == that.flattened
+ && isGreedy == that.isGreedy
+ && requiresLength == that.requiresLength
+ && Objects.equals(unmarshallLocationName, that.unmarshallLocationName)
+ && Objects.equals(marshallLocationName, that.marshallLocationName)
+ && Objects.equals(additionalUnmarshallingPath, that.additionalUnmarshallingPath)
+ && Objects.equals(additionalMarshallingPath, that.additionalMarshallingPath)
+ && location == that.location;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(unmarshallLocationName);
+ result = 31 * result + Objects.hashCode(marshallLocationName);
+ result = 31 * result + Objects.hashCode(additionalUnmarshallingPath);
+ result = 31 * result + Objects.hashCode(additionalMarshallingPath);
+ result = 31 * result + Boolean.hashCode(isPayload);
+ result = 31 * result + Boolean.hashCode(isStreaming);
+ result = 31 * result + Objects.hashCode(location);
+ result = 31 * result + Boolean.hashCode(flattened);
+ result = 31 * result + Boolean.hashCode(isGreedy);
+ result = 31 * result + Boolean.hashCode(requiresLength);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ReturnTypeModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ReturnTypeModel.java
index 77dff4c71481..1d46c2802cda 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ReturnTypeModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ReturnTypeModel.java
@@ -15,6 +15,8 @@
package software.amazon.awssdk.codegen.model.intermediate;
+import java.util.Objects;
+
public class ReturnTypeModel {
private String returnType;
@@ -48,4 +50,24 @@ public ReturnTypeModel withDocumentation(String documentation) {
setDocumentation(documentation);
return this;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ReturnTypeModel that = (ReturnTypeModel) o;
+ return Objects.equals(returnType, that.returnType) && Objects.equals(documentation, that.documentation);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(returnType);
+ result = 31 * result + Objects.hashCode(documentation);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ShapeModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ShapeModel.java
index 098ea46bc7e4..3c26965302d5 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ShapeModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ShapeModel.java
@@ -26,6 +26,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.stream.Collectors;
import software.amazon.awssdk.codegen.model.intermediate.customization.ShapeCustomizationInfo;
import software.amazon.awssdk.codegen.model.service.XmlNamespace;
@@ -669,4 +670,84 @@ public ShapeModel withIsThrottling(boolean throttling) {
this.throttling = throttling;
return this;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ ShapeModel that = (ShapeModel) o;
+ return deprecated == that.deprecated
+ && hasPayloadMember == that.hasPayloadMember
+ && hasHeaderMember == that.hasHeaderMember
+ && hasStatusCodeMember == that.hasStatusCodeMember
+ && hasStreamingMember == that.hasStreamingMember
+ && hasRequiresLengthMember == that.hasRequiresLengthMember
+ && wrapper == that.wrapper
+ && simpleMethod == that.simpleMethod
+ && fault == that.fault
+ && isEventStream == that.isEventStream
+ && isEvent == that.isEvent
+ && document == that.document
+ && union == that.union
+ && retryable == that.retryable
+ && throttling == that.throttling
+ && Objects.equals(c2jName, that.c2jName)
+ && Objects.equals(shapeName, that.shapeName)
+ && Objects.equals(deprecatedMessage, that.deprecatedMessage)
+ && Objects.equals(type, that.type)
+ && Objects.equals(required, that.required)
+ && Objects.equals(requestSignerClassFqcn, that.requestSignerClassFqcn)
+ && Objects.equals(endpointDiscovery, that.endpointDiscovery)
+ && Objects.equals(members, that.members)
+ && Objects.equals(enums, that.enums)
+ && Objects.equals(variable, that.variable)
+ && Objects.equals(marshaller, that.marshaller)
+ && Objects.equals(unmarshaller, that.unmarshaller)
+ && Objects.equals(errorCode, that.errorCode)
+ && Objects.equals(httpStatusCode, that.httpStatusCode)
+ && Objects.equals(customization, that.customization)
+ && Objects.equals(xmlNamespace, that.xmlNamespace);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + Objects.hashCode(c2jName);
+ result = 31 * result + Objects.hashCode(shapeName);
+ result = 31 * result + Boolean.hashCode(deprecated);
+ result = 31 * result + Objects.hashCode(deprecatedMessage);
+ result = 31 * result + Objects.hashCode(type);
+ result = 31 * result + Objects.hashCode(required);
+ result = 31 * result + Boolean.hashCode(hasPayloadMember);
+ result = 31 * result + Boolean.hashCode(hasHeaderMember);
+ result = 31 * result + Boolean.hashCode(hasStatusCodeMember);
+ result = 31 * result + Boolean.hashCode(hasStreamingMember);
+ result = 31 * result + Boolean.hashCode(hasRequiresLengthMember);
+ result = 31 * result + Boolean.hashCode(wrapper);
+ result = 31 * result + Boolean.hashCode(simpleMethod);
+ result = 31 * result + Objects.hashCode(requestSignerClassFqcn);
+ result = 31 * result + Objects.hashCode(endpointDiscovery);
+ result = 31 * result + Objects.hashCode(members);
+ result = 31 * result + Objects.hashCode(enums);
+ result = 31 * result + Objects.hashCode(variable);
+ result = 31 * result + Objects.hashCode(marshaller);
+ result = 31 * result + Objects.hashCode(unmarshaller);
+ result = 31 * result + Objects.hashCode(errorCode);
+ result = 31 * result + Objects.hashCode(httpStatusCode);
+ result = 31 * result + Boolean.hashCode(fault);
+ result = 31 * result + Objects.hashCode(customization);
+ result = 31 * result + Boolean.hashCode(isEventStream);
+ result = 31 * result + Boolean.hashCode(isEvent);
+ result = 31 * result + Objects.hashCode(xmlNamespace);
+ result = 31 * result + Boolean.hashCode(document);
+ result = 31 * result + Boolean.hashCode(union);
+ result = 31 * result + Boolean.hashCode(retryable);
+ result = 31 * result + Boolean.hashCode(throttling);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/VariableModel.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/VariableModel.java
index bdf0668a9d21..b9355009e748 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/VariableModel.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/VariableModel.java
@@ -17,6 +17,7 @@
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
public class VariableModel extends DocumentationModel {
@@ -98,4 +99,31 @@ public String getVariableSetterType() {
public String toString() {
return variableName;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ VariableModel that = (VariableModel) o;
+ return Objects.equals(variableName, that.variableName)
+ && Objects.equals(variableType, that.variableType)
+ && Objects.equals(variableDeclarationType, that.variableDeclarationType);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + Objects.hashCode(variableName);
+ result = 31 * result + Objects.hashCode(variableType);
+ result = 31 * result + Objects.hashCode(variableDeclarationType);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/customization/ArtificialResultWrapper.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/customization/ArtificialResultWrapper.java
index e8adab25b48c..dd0b91d86301 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/customization/ArtificialResultWrapper.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/customization/ArtificialResultWrapper.java
@@ -15,6 +15,8 @@
package software.amazon.awssdk.codegen.model.intermediate.customization;
+import java.util.Objects;
+
public class ArtificialResultWrapper {
private String wrappedMemberName;
private String wrappedMemberSimpleType;
@@ -34,4 +36,25 @@ public String getWrappedMemberSimpleType() {
public void setWrappedMemberSimpleType(String wrappedMemberSimpleType) {
this.wrappedMemberSimpleType = wrappedMemberSimpleType;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ArtificialResultWrapper that = (ArtificialResultWrapper) o;
+ return Objects.equals(wrappedMemberName, that.wrappedMemberName)
+ && Objects.equals(wrappedMemberSimpleType, that.wrappedMemberSimpleType);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(wrappedMemberName);
+ result = 31 * result + Objects.hashCode(wrappedMemberSimpleType);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/customization/ShapeCustomizationInfo.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/customization/ShapeCustomizationInfo.java
index b6d3950985b2..2e031eabb9a4 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/customization/ShapeCustomizationInfo.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/customization/ShapeCustomizationInfo.java
@@ -16,6 +16,7 @@
package software.amazon.awssdk.codegen.model.intermediate.customization;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.Objects;
public class ShapeCustomizationInfo {
@@ -72,4 +73,33 @@ public void setStaxTargetDepthOffset(int staxTargetDepthOffset) {
public boolean hasStaxTargetDepthOffset() {
return hasStaxTargetDepthOffset;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ShapeCustomizationInfo that = (ShapeCustomizationInfo) o;
+ return skipGeneratingModelClass == that.skipGeneratingModelClass
+ && skipGeneratingMarshaller == that.skipGeneratingMarshaller
+ && skipGeneratingUnmarshaller == that.skipGeneratingUnmarshaller
+ && staxTargetDepthOffset == that.staxTargetDepthOffset
+ && hasStaxTargetDepthOffset == that.hasStaxTargetDepthOffset
+ && Objects.equals(artificialResultWrapper, that.artificialResultWrapper);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(artificialResultWrapper);
+ result = 31 * result + Boolean.hashCode(skipGeneratingModelClass);
+ result = 31 * result + Boolean.hashCode(skipGeneratingMarshaller);
+ result = 31 * result + Boolean.hashCode(skipGeneratingUnmarshaller);
+ result = 31 * result + staxTargetDepthOffset;
+ result = 31 * result + Boolean.hashCode(hasStaxTargetDepthOffset);
+ return result;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/service/ContextParam.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/service/ContextParam.java
index 96f363cd84f1..8650d1145bcb 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/service/ContextParam.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/service/ContextParam.java
@@ -15,6 +15,8 @@
package software.amazon.awssdk.codegen.model.service;
+import java.util.Objects;
+
public class ContextParam {
private String name;
@@ -25,4 +27,22 @@ public String getName() {
public void setName(String name) {
this.name = name;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ContextParam that = (ContextParam) o;
+ return Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name);
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/validation/SharedModelsValidator.java b/codegen/src/main/java/software/amazon/awssdk/codegen/validation/SharedModelsValidator.java
new file mode 100644
index 000000000000..6b7f8471da7c
--- /dev/null
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/validation/SharedModelsValidator.java
@@ -0,0 +1,210 @@
+/*
+ * 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.validation;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
+import software.amazon.awssdk.codegen.model.intermediate.ListModel;
+import software.amazon.awssdk.codegen.model.intermediate.MapModel;
+import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
+import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
+import software.amazon.awssdk.utils.Logger;
+
+/**
+ * Validator that ensures any shapes shared between two services are completely identical. This validator returns a validation
+ * entry for each shape that is present in both service models but has differing definitions in each model.
+ */
+public final class SharedModelsValidator implements ModelValidator {
+ private static final Logger LOG = Logger.loggerFor(SharedModelsValidator.class);
+
+ @Override
+ public List validateModels(ModelValidationContext context) {
+ if (!context.shareModelsTarget().isPresent()) {
+ return Collections.emptyList();
+ }
+
+ return validateSharedShapes(context.intermediateModel(), context.shareModelsTarget().get());
+ }
+
+ private List validateSharedShapes(IntermediateModel m1, IntermediateModel m2) {
+ List errors = new ArrayList<>();
+
+ Map m1Shapes = m1.getShapes();
+ Map m2Shapes = m2.getShapes();
+
+ m1Shapes.forEach((name, m1Shape) -> {
+ if (!m2Shapes.containsKey(name)) {
+ return;
+ }
+
+ ShapeModel m2Shape = m2Shapes.get(name);
+
+ if (!shapesAreIdentical(m1Shape, m2Shape)) {
+ String detailMsg = String.format("Services '%s' and '%s' have differing definitions of the shared model '%s'",
+ m1.getMetadata().getServiceName(),
+ m2.getMetadata().getServiceName(),
+ name);
+ LOG.warn(() -> detailMsg);
+
+ errors.add(new ValidationEntry().withErrorId(ValidationErrorId.SHARED_MODELS_DIFFER)
+ .withSeverity(ValidationErrorSeverity.DANGER)
+ .withDetailMessage(detailMsg));
+ }
+ });
+
+ return errors;
+ }
+
+ private boolean shapesAreIdentical(ShapeModel m1, ShapeModel m2) {
+ // Note: We can't simply do m1.equals(m2) because shared models can still differ slightly in the
+ // marshalling/unmarshalling info such as the exact request operation name on the wire.
+ // In particular, we leave out comparing the `unmarshaller` and `marshaller` members of ShapeModel.
+ // Additionally, the List are not compared with equals() because we handle MemberModel equality specially
+ // as well.
+ return m1.isDeprecated() == m2.isDeprecated()
+ && m1.isHasPayloadMember() == m2.isHasPayloadMember()
+ && m1.isHasHeaderMember() == m2.isHasHeaderMember()
+ && m1.isHasStatusCodeMember() == m2.isHasStatusCodeMember()
+ && m1.isHasStreamingMember() == m2.isHasStreamingMember()
+ && m1.isHasRequiresLengthMember() == m2.isHasRequiresLengthMember()
+ && m1.isWrapper() == m2.isWrapper()
+ && m1.isSimpleMethod() == m2.isSimpleMethod()
+ && m1.isFault() == m2.isFault()
+ && m1.isEventStream() == m2.isEventStream()
+ && m1.isEvent() == m2.isEvent()
+ && m1.isDocument() == m2.isDocument()
+ && m1.isUnion() == m2.isUnion()
+ && m1.isRetryable() == m2.isRetryable()
+ && m1.isThrottling() == m2.isThrottling()
+ && Objects.equals(m1.getC2jName(), m2.getC2jName())
+ && Objects.equals(m1.getShapeName(), m2.getShapeName())
+ && Objects.equals(m1.getDeprecatedMessage(), m2.getDeprecatedMessage())
+ && Objects.equals(m1.getType(), m2.getType())
+ && Objects.equals(m1.getRequired(), m2.getRequired())
+ && Objects.equals(m1.getRequestSignerClassFqcn(), m2.getRequestSignerClassFqcn())
+ && Objects.equals(m1.getEndpointDiscovery(), m2.getEndpointDiscovery())
+ && memberListsAreIdentical(m1.getMembers(), m2.getMembers())
+ && Objects.equals(m1.getEnums(), m2.getEnums())
+ && Objects.equals(m1.getVariable(), m2.getVariable())
+ && Objects.equals(m1.getErrorCode(), m2.getErrorCode())
+ && Objects.equals(m1.getHttpStatusCode(), m2.getHttpStatusCode())
+ && Objects.equals(m1.getCustomization(), m2.getCustomization())
+ && Objects.equals(m1.getXmlNamespace(), m2.getXmlNamespace())
+ ;
+ }
+
+ private boolean memberListsAreIdentical(List memberList1, List memberList2) {
+ if (memberList1.size() != memberList2.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < memberList1.size(); i++) {
+ MemberModel m1 = memberList1.get(i);
+ MemberModel m2 = memberList2.get(i);
+ if (!memberModelsAreIdentical(m1, m2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean memberModelsAreIdentical(MemberModel m1, MemberModel m2) {
+ // Similar to ShapeModel, can't call equals() directly. It has a ShapeModel property that is ignored, and ListModel and
+ // MapModel are treated similarly
+ return m1.isDeprecated() == m2.isDeprecated()
+ && m1.isRequired() == m2.isRequired()
+ && m1.isSynthetic() == m2.isSynthetic()
+ && m1.isIdempotencyToken() == m2.isIdempotencyToken()
+ && m1.isJsonValue() == m2.isJsonValue()
+ && m1.isEventPayload() == m2.isEventPayload()
+ && m1.isEventHeader() == m2.isEventHeader()
+ && m1.isEndpointDiscoveryId() == m2.isEndpointDiscoveryId()
+ && m1.isSensitive() == m2.isSensitive()
+ && m1.isXmlAttribute() == m2.isXmlAttribute()
+ && m1.ignoreDataTypeConversionFailures() == m2.ignoreDataTypeConversionFailures()
+ && Objects.equals(m1.getName(), m2.getName())
+ && Objects.equals(m1.getC2jName(), m2.getC2jName())
+ && Objects.equals(m1.getC2jShape(), m2.getC2jShape())
+ && Objects.equals(m1.getVariable(), m2.getVariable())
+ && Objects.equals(m1.getSetterModel(), m2.getSetterModel())
+ && Objects.equals(m1.getGetterModel(), m2.getGetterModel())
+ && Objects.equals(m1.getHttp(), m2.getHttp())
+ && Objects.equals(m1.getDeprecatedMessage(), m2.getDeprecatedMessage())
+ // Note: not equals()
+ && listModelsAreIdentical(m1.getListModel(), m2.getListModel())
+ // Note: not equals()
+ && mapModelsAreIdentical(m1.getMapModel(), m2.getMapModel())
+ && Objects.equals(m1.getEnumType(), m2.getEnumType())
+ && Objects.equals(m1.getXmlNameSpaceUri(), m2.getXmlNameSpaceUri())
+ && Objects.equals(m1.getFluentGetterMethodName(), m2.getFluentGetterMethodName())
+ && Objects.equals(m1.getFluentEnumGetterMethodName(), m2.getFluentEnumGetterMethodName())
+ && Objects.equals(m1.getFluentSetterMethodName(), m2.getFluentSetterMethodName())
+ && Objects.equals(m1.getFluentEnumSetterMethodName(), m2.getFluentEnumSetterMethodName())
+ && Objects.equals(m1.getExistenceCheckMethodName(), m2.getExistenceCheckMethodName())
+ && Objects.equals(m1.getBeanStyleGetterMethodName(), m2.getBeanStyleGetterMethodName())
+ && Objects.equals(m1.getBeanStyleSetterMethodName(), m2.getBeanStyleSetterMethodName())
+ && Objects.equals(m1.getUnionEnumTypeName(), m2.getUnionEnumTypeName())
+ && Objects.equals(m1.getTimestampFormat(), m2.getTimestampFormat())
+ && Objects.equals(m1.getDeprecatedName(), m2.getDeprecatedName())
+ && Objects.equals(m1.getDeprecatedFluentGetterMethodName(), m2.getDeprecatedFluentGetterMethodName())
+ && Objects.equals(m1.getDeprecatedFluentSetterMethodName(), m2.getDeprecatedFluentSetterMethodName())
+ && Objects.equals(m1.getDeprecatedBeanStyleSetterMethodName(), m2.getDeprecatedBeanStyleSetterMethodName())
+ && Objects.equals(m1.getContextParam(), m2.getContextParam());
+ }
+
+ private boolean listModelsAreIdentical(ListModel m1, ListModel m2) {
+ if (m1 == null ^ m2 == null) {
+ return false;
+ }
+
+ if (m1 == null) {
+ return true;
+ }
+
+ return Objects.equals(m1.getImplType(), m2.getImplType())
+ && Objects.equals(m1.getMemberType(), m2.getMemberType())
+ && Objects.equals(m1.getInterfaceType(), m2.getInterfaceType())
+ // Note: not equals()
+ && memberModelsAreIdentical(m1.getListMemberModel(), m2.getListMemberModel())
+ && Objects.equals(m1.getMemberLocationName(), m2.getMemberLocationName())
+ && Objects.equals(m1.getMemberAdditionalMarshallingPath(), m2.getMemberAdditionalMarshallingPath())
+ && Objects.equals(m1.getMemberAdditionalUnmarshallingPath(), m2.getMemberAdditionalUnmarshallingPath());
+ }
+
+ private boolean mapModelsAreIdentical(MapModel m1, MapModel m2) {
+ if (m1 == null ^ m2 == null) {
+ return false;
+ }
+
+ if (m1 == null) {
+ return true;
+ }
+
+ return Objects.equals(m1.getImplType(), m2.getImplType())
+ && Objects.equals(m1.getInterfaceType(), m2.getInterfaceType())
+ && Objects.equals(m1.getKeyLocationName(), m2.getKeyLocationName())
+ // Note: not equals()
+ && memberModelsAreIdentical(m1.getKeyModel(), m2.getKeyModel())
+ && Objects.equals(m1.getValueLocationName(), m2.getValueLocationName())
+ // Note: not equals()
+ && memberModelsAreIdentical(m1.getValueModel(), m2.getValueModel());
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ArgumentModelTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ArgumentModelTest.java
new file mode 100644
index 000000000000..107a6e6cdb66
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ArgumentModelTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.intermediate;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class ArgumentModelTest {
+ @Test
+ public void equals_isCorrect() {
+ EqualsVerifier.simple()
+ .forClass(ArgumentModel.class)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/AuthorizerModelTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/AuthorizerModelTest.java
new file mode 100644
index 000000000000..28d8dd845412
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/AuthorizerModelTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.intermediate;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class AuthorizerModelTest {
+ @Test
+ public void equals_isCorrect() {
+ EqualsVerifier.simple().forClass(AuthorizerModel.class).usingGetClass().verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/MemberModelTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/MemberModelTest.java
new file mode 100644
index 000000000000..bd4a0859603f
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/MemberModelTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.intermediate;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class MemberModelTest {
+ @Test
+ public void equals_isCorrect() {
+ ListModel redListModel = new ListModel();
+ redListModel.setMemberLocationName("RedLocation");
+ ListModel blueListModel = new ListModel();
+ blueListModel.setMemberLocationName("BlueLocation");
+
+ MemberModel redMemberModel = new MemberModel();
+ redMemberModel.setC2jName("RedC2jName");
+ MemberModel blueMemberModel = new MemberModel();
+ blueMemberModel.setC2jName("BlueC2jName");
+
+ EqualsVerifier.simple().forClass(MemberModel.class)
+ .withPrefabValues(ListModel.class, redListModel, blueListModel)
+ .withPrefabValues(MemberModel.class, redMemberModel, blueMemberModel)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/OperationModelTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/OperationModelTest.java
new file mode 100644
index 000000000000..531d0b1aa55e
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/OperationModelTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.intermediate;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class OperationModelTest {
+ @Test
+ void equals_isCorrect() {
+ MemberModel blueMemberModel = new MemberModel();
+ blueMemberModel.setName("blue");
+ MemberModel redMemberModel = new MemberModel();
+ redMemberModel.setName("red");
+
+ EqualsVerifier.simple()
+ .forClass(OperationModel.class)
+ .withPrefabValues(MemberModel.class, blueMemberModel, redMemberModel)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ParameterHttpMappingTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ParameterHttpMappingTest.java
new file mode 100644
index 000000000000..cd142cb34c2c
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ParameterHttpMappingTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.intermediate;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class ParameterHttpMappingTest {
+ @Test
+ void equals_isCorrect() {
+ EqualsVerifier.simple()
+ .forClass(ParameterHttpMapping.class)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ReturnTypeModelTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ReturnTypeModelTest.java
new file mode 100644
index 000000000000..53e99f514403
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ReturnTypeModelTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.intermediate;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class ReturnTypeModelTest {
+ @Test
+ void equals_isCorrect() {
+ EqualsVerifier.simple()
+ .forClass(ReturnTypeModel.class)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ShapeModelTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ShapeModelTest.java
new file mode 100644
index 000000000000..08fb79681e96
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/ShapeModelTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.intermediate;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class ShapeModelTest {
+
+
+ @Test
+ public void equals_isCorrect() {
+ MemberModel blueMemberModel = new MemberModel();
+ blueMemberModel.setName("blue");
+ MemberModel redMemberModel = new MemberModel();
+ redMemberModel.setName("red");
+
+ EqualsVerifier.simple()
+ .forClass(ShapeModel.class)
+ .withPrefabValues(MemberModel.class, blueMemberModel, redMemberModel)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/VariableModelTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/VariableModelTest.java
new file mode 100644
index 000000000000..55ea2f39123a
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/VariableModelTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.intermediate;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class VariableModelTest {
+ @Test
+ void equals_isCorrect() {
+ EqualsVerifier.simple()
+ .forClass(VariableModel.class)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/customization/ArtificialResultWrapperTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/customization/ArtificialResultWrapperTest.java
new file mode 100644
index 000000000000..aa29412a5f5f
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/customization/ArtificialResultWrapperTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.intermediate.customization;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class ArtificialResultWrapperTest {
+ @Test
+ void equals_isCorrect() {
+ EqualsVerifier.simple()
+ .forClass(ArtificialResultWrapper.class)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/customization/ShapeCustomizationInfoTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/customization/ShapeCustomizationInfoTest.java
new file mode 100644
index 000000000000..3126117f100d
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/customization/ShapeCustomizationInfoTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.intermediate.customization;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class ShapeCustomizationInfoTest {
+ @Test
+ void equals_isCorrect() {
+ EqualsVerifier.simple()
+ .forClass(ShapeCustomizationInfo.class)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/service/ContextParamTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/service/ContextParamTest.java
new file mode 100644
index 000000000000..937688b70cb4
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/service/ContextParamTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.service;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.jupiter.api.Test;
+
+public class ContextParamTest {
+ @Test
+ void equals_isCorrect() {
+ EqualsVerifier.simple()
+ .forClass(ContextParam.class)
+ .usingGetClass()
+ .verify();
+ }
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/validation/SharedModelsValidatorTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/validation/SharedModelsValidatorTest.java
new file mode 100644
index 000000000000..a485956d94bc
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/validation/SharedModelsValidatorTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.validation;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
+import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
+import software.amazon.awssdk.codegen.model.intermediate.Metadata;
+import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
+import software.amazon.awssdk.codegen.poet.ClientTestModels;
+
+public class SharedModelsValidatorTest {
+ private final ModelValidator validator = new SharedModelsValidator();
+
+ @Test
+ void validateModels_noTargetService_noValidationErrors() {
+ assertThat(runValidation(ClientTestModels.awsJsonServiceModels(), null)).isEmpty();
+ }
+
+ @Test
+ void validateModels_targetServiceTriviallyIdentical_noValidationErrors() {
+ assertThat(runValidation(ClientTestModels.awsJsonServiceModels(), ClientTestModels.awsJsonServiceModels())).isEmpty();
+ }
+
+ @Test
+ void validateModels_noSharedShapes_noValidationErrors() {
+ IntermediateModel target = ClientTestModels.awsJsonServiceModels();
+ Map renamedShapes = target.getShapes()
+ .entrySet()
+ .stream()
+ .collect(Collectors.toMap(e -> "Copy" + e.getKey(), Map.Entry::getValue));
+ target.setShapes(renamedShapes);
+
+ assertThat(runValidation(ClientTestModels.awsJsonServiceModels(), target)).isEmpty();
+ }
+
+ @Test
+ void validateModels_sharedShapesNotIdentical_emitsValidationError() {
+ IntermediateModel target = ClientTestModels.awsJsonServiceModels();
+ Map modifiedShapes = target.getShapes()
+ .entrySet()
+ .stream()
+ .collect(Collectors.toMap(Map.Entry::getKey,
+ e -> {
+ ShapeModel shapeModel = e.getValue();
+ shapeModel.setDeprecated(!shapeModel.isDeprecated());
+ return shapeModel;
+ }));
+
+ target.setShapes(modifiedShapes);
+
+ List validationEntries = runValidation(ClientTestModels.awsJsonServiceModels(), target);
+
+ assertThat(validationEntries).hasSize(modifiedShapes.size());
+
+ assertThat(validationEntries).allMatch(e -> e.getErrorId() == ValidationErrorId.SHARED_MODELS_DIFFER
+ && e.getSeverity() == ValidationErrorSeverity.DANGER);
+ }
+
+ @Test
+ void validateModels_shapesDontHaveSameMemberNames_emitsValidationError() {
+ IntermediateModel fooService = new IntermediateModel();
+ fooService.setMetadata(new Metadata().withServiceName("Foo"));
+
+ IntermediateModel barService = new IntermediateModel();
+ barService.setMetadata(new Metadata().withServiceName("Bar"));
+
+ String shapeName = "TestShape";
+
+ ShapeModel shape1 = new ShapeModel();
+ MemberModel member1 = new MemberModel();
+ member1.setName("Shape1Member");
+ shape1.setMembers(Arrays.asList(member1));
+
+ ShapeModel shape2 = new ShapeModel();
+ MemberModel member2 = new MemberModel();
+ member2.setName("Shape2Member");
+ shape2.setMembers(Arrays.asList(member2));
+
+ Map fooServiceShapes = new HashMap<>();
+ fooServiceShapes.put(shapeName, shape1);
+ fooService.setShapes(fooServiceShapes);
+
+ Map barServiceShapes = new HashMap<>();
+ barServiceShapes.put(shapeName, shape2);
+ barService.setShapes(barServiceShapes);
+
+ List validationEntries = runValidation(fooService, barService);
+
+ assertThat(validationEntries).hasSize(1);
+ }
+
+ @Test
+ void validateModels_shapesDontHaveSameMembers_emitsValidationError() {
+ IntermediateModel fooService = new IntermediateModel();
+ fooService.setMetadata(new Metadata().withServiceName("Foo"));
+
+ IntermediateModel barService = new IntermediateModel();
+ barService.setMetadata(new Metadata().withServiceName("Bar"));
+
+ String shapeName = "TestShape";
+ ShapeModel shape1 = new ShapeModel();
+
+ ShapeModel shape2 = new ShapeModel();
+ shape2.setMembers(Arrays.asList(new MemberModel(), new MemberModel()));
+
+ Map fooServiceShapes = new HashMap<>();
+ fooServiceShapes.put(shapeName, shape1);
+ fooService.setShapes(fooServiceShapes);
+
+ Map barServiceShapes = new HashMap<>();
+ barServiceShapes.put(shapeName, shape2);
+ barService.setShapes(barServiceShapes);
+
+ List validationEntries = runValidation(fooService, barService);
+
+ assertThat(validationEntries).hasSize(1);
+ }
+
+ private List runValidation(IntermediateModel m1, IntermediateModel m2) {
+ ModelValidationContext ctx = ModelValidationContext.builder()
+ .intermediateModel(m1)
+ .shareModelsTarget(m2)
+ .build();
+
+ return validator.validateModels(ctx);
+ }
+}