Skip to content

Add support for service model protocols field #6161

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

Merged
merged 9 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-4a7af00.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Add support for protocols field in service model"
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.naming.DefaultNamingStrategy;
import software.amazon.awssdk.codegen.naming.NamingStrategy;
import software.amazon.awssdk.codegen.utils.ProtocolUtils;
import software.amazon.awssdk.utils.Pair;
import software.amazon.awssdk.utils.StringUtils;

Expand Down Expand Up @@ -70,7 +71,7 @@ public static Metadata constructMetadata(ServiceModel serviceModel,
.withBaseExceptionName(String.format(Constant.BASE_EXCEPTION_NAME_PATTERN, serviceName))
.withBaseRequestName(String.format(Constant.BASE_REQUEST_NAME_PATTERN, serviceName))
.withBaseResponseName(String.format(Constant.BASE_RESPONSE_NAME_PATTERN, serviceName))
.withProtocol(Protocol.fromValue(serviceMetadata.getProtocol()))
.withProtocol(Protocol.fromValue(ProtocolUtils.resolveProtocol(serviceMetadata)))
.withJsonVersion(serviceMetadata.getJsonVersion())
.withEndpointPrefix(serviceMetadata.getEndpointPrefix())
.withSigningName(serviceMetadata.getSigningName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.model.service.Shape;
import software.amazon.awssdk.codegen.naming.NamingStrategy;
import software.amazon.awssdk.codegen.utils.ProtocolUtils;

/**
* Constructs the operation model for every operation defined by the service.
Expand Down Expand Up @@ -164,7 +165,7 @@ public Map<String, OperationModel> constructOperations() {
OperationModel operationModel = new OperationModel();

operationModel.setOperationName(operationName);
operationModel.setServiceProtocol(serviceModel.getMetadata().getProtocol());
operationModel.setServiceProtocol(ProtocolUtils.resolveProtocol(serviceModel.getMetadata()));
operationModel.setDeprecated(op.isDeprecated());
operationModel.setDeprecatedMessage(op.getDeprecatedMessage());
operationModel.setDocumentation(op.getDocumentation());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.model.service.Shape;
import software.amazon.awssdk.codegen.naming.NamingStrategy;
import software.amazon.awssdk.codegen.utils.ProtocolUtils;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.Validate;

Expand Down Expand Up @@ -464,6 +465,6 @@ private void fillContainerTypeMemberMetadata(Map<String, Shape> c2jShapes,
}

protected String getProtocol() {
return getServiceModel().getMetadata().getProtocol();
return ProtocolUtils.resolveProtocol(getServiceModel().getMetadata());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import software.amazon.awssdk.codegen.model.service.Output;
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.model.service.Shape;
import software.amazon.awssdk.codegen.utils.ProtocolUtils;

/**
* Operations with explicit String payloads are not supported for services with Query protocol. We fail the codegen if the
Expand All @@ -31,7 +32,7 @@
public class ExplicitStringPayloadQueryProtocolProcessor implements CodegenCustomizationProcessor {
@Override
public void preprocess(ServiceModel serviceModel) {
String protocol = serviceModel.getMetadata().getProtocol();
String protocol = ProtocolUtils.resolveProtocol(serviceModel.getMetadata());
if (!"ec2".equals(protocol) && !"query".equals(protocol)) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

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

import java.util.Collections;
import software.amazon.awssdk.codegen.customization.CodegenCustomizationProcessor;
import software.amazon.awssdk.codegen.model.config.customization.MetadataConfig;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
Expand Down Expand Up @@ -44,7 +45,7 @@ public void preprocess(ServiceModel serviceModel) {

String customProtocol = metadataConfig.getProtocol();
if (customProtocol != null) {
serviceMetadata.setProtocol(customProtocol);
serviceMetadata.setProtocols(Collections.singletonList(customProtocol));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import software.amazon.awssdk.codegen.model.service.Http;
import software.amazon.awssdk.codegen.model.service.Operation;
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.utils.ProtocolUtils;

/**
* This processor only runs for services using the <code>smithy-rpc-v2-cbor</code> protocol.
Expand All @@ -29,7 +30,8 @@
public class SmithyRpcV2CborProtocolProcessor implements CodegenCustomizationProcessor {
@Override
public void preprocess(ServiceModel serviceModel) {
if (!"smithy-rpc-v2-cbor".equals(serviceModel.getMetadata().getProtocol())) {
String protocol = ProtocolUtils.resolveProtocol(serviceModel.getMetadata());
if (!"smithy-rpc-v2-cbor".equals(protocol)) {
return;
}
serviceModel.getOperations().forEach((name, op) -> setRequestUri(serviceModel, name, op));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.model.service.Shape;
import software.amazon.awssdk.codegen.model.service.XmlNamespace;
import software.amazon.awssdk.codegen.utils.ProtocolUtils;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.StringUtils;

Expand Down Expand Up @@ -331,11 +332,13 @@ public static ShapeMarshaller createInputShapeMarshaller(ServiceMetadata service
"The operation parameter must be specified!");
}

String protocol = ProtocolUtils.resolveProtocol(service);

ShapeMarshaller marshaller = new ShapeMarshaller()
.withAction(operation.getName())
.withVerb(operation.getHttp().getMethod())
.withRequestUri(operation.getHttp().getRequestUri())
.withProtocol(service.getProtocol());
.withProtocol(protocol);
Input input = operation.getInput();
if (input != null) {
marshaller.setLocationName(input.getLocationName());
Expand All @@ -345,7 +348,7 @@ public static ShapeMarshaller createInputShapeMarshaller(ServiceMetadata service
marshaller.setXmlNameSpaceUri(xmlNamespace.getUri());
}
}
if (Metadata.usesOperationIdentifier(service.getProtocol())) {
if (Metadata.usesOperationIdentifier(protocol)) {
marshaller.setTarget(StringUtils.isEmpty(service.getTargetPrefix()) ?
operation.getName() :
service.getTargetPrefix() + "." + operation.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.List;
import java.util.Map;
import software.amazon.awssdk.codegen.utils.ProtocolUtils;

public class ServiceMetadata {

Expand All @@ -36,6 +37,8 @@ public class ServiceMetadata {

private String protocol;

private List<String> protocols;

private String jsonVersion;

private Map<String, String> awsQueryCompatible;
Expand Down Expand Up @@ -103,14 +106,30 @@ public void setXmlNamespace(String xmlNamespace) {
this.xmlNamespace = xmlNamespace;
}

/**
* {@code protocol} superseded by {@code protocols} field, resolved in {@link ProtocolUtils#resolveProtocol(ServiceMetadata)}
*/
@Deprecated
public String getProtocol() {
return protocol;
}

/**
* {@code protocol} superseded by {@code protocols} field, resolved in {@link ProtocolUtils#resolveProtocol(ServiceMetadata)}
*/
@Deprecated
public void setProtocol(String protocol) {
this.protocol = protocol;
}

public List<String> getProtocols() {
return protocols;
}

public void setProtocols(List<String> protocols) {
this.protocols = protocols;
}

public String getJsonVersion() {
return jsonVersion;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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.utils;

import java.util.Arrays;
import java.util.List;
import software.amazon.awssdk.codegen.model.service.ServiceMetadata;

/**
* Resolves the protocol from the service model {@code protocol} and {@code protocols} fields.
*/
public final class ProtocolUtils {

/**
* Priority-ordered list of protocols supported by the SDK.
*/
private static final List<String> SUPPORTED_PROTOCOLS = Arrays.asList(
"smithy-rpc-v2-cbor", "json", "rest-json", "rest-xml", "query", "ec2");

private ProtocolUtils() {
}

/**
* {@code protocols} supersedes {@code protocol}. The highest priority protocol supported by the SDK that is present in the
* service model {@code protocols} list will be selected. If none of the values in {@code protocols} is supported by the
* SDK, an error will be thrown. If {@code protocols} is empty or null, the value from {@code protocol} will be returned.
*/
public static String resolveProtocol(ServiceMetadata serviceMetadata) {

List<String> protocols = serviceMetadata.getProtocols();
String protocol = serviceMetadata.getProtocol();

if (protocols == null || protocols.isEmpty()) {
return protocol;
}

// Kinesis uses customization.config customServiceMetadata to set cbor
if ("cbor".equals(protocols.get(0))) {
return "cbor";
}

for (String supportedProtocol : SUPPORTED_PROTOCOLS) {
if (protocols.contains(supportedProtocol)) {
return supportedProtocol;
}
}

throw new IllegalArgumentException("The SDK does not support any of provided protocols: " + protocols);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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.utils;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import software.amazon.awssdk.codegen.model.service.ServiceMetadata;

public class ProtocolUtilsTest {

@ParameterizedTest
@MethodSource("protocolsValues")
public void protocolSelection(List<String> protocols, String expectedProtocol) {
ServiceMetadata serviceMetadata = serviceMetadata(protocols);
String selectedProtocol = ProtocolUtils.resolveProtocol(serviceMetadata);
assertThat(selectedProtocol).isEqualTo(expectedProtocol);
}

@Test
public void emptyProtocolsWithPresentProtocol() {
ServiceMetadata serviceMetadata = new ServiceMetadata();
serviceMetadata.setProtocol("json");
String selectedProtocol = ProtocolUtils.resolveProtocol(serviceMetadata);
assertThat(selectedProtocol).isEqualTo("json");
}

@Test
public void protocolsWithJson_protocolCbor_selectsJson() {
ServiceMetadata serviceMetadata = new ServiceMetadata();
serviceMetadata.setProtocols(Collections.singletonList("json"));
serviceMetadata.setProtocol("smithy-rpc-v2-cbor");
String selectedProtocol = ProtocolUtils.resolveProtocol(serviceMetadata);
assertThat(selectedProtocol).isEqualTo("json");
}

private static Stream<Arguments> protocolsValues() {
return Stream.of(Arguments.of(Arrays.asList("smithy-rpc-v2-cbor", "json"), "smithy-rpc-v2-cbor"),
Arguments.of(Collections.singletonList("smithy-rpc-v2-cbor"), "smithy-rpc-v2-cbor"),
Arguments.of(Arrays.asList("smithy-rpc-v2-cbor", "json", "query"), "smithy-rpc-v2-cbor"),
Arguments.of(Arrays.asList("json", "query"), "json"),
Arguments.of(Collections.singletonList("query"), "query"));
}

private static ServiceMetadata serviceMetadata(List<String> protocols) {
ServiceMetadata serviceMetadata = new ServiceMetadata();
serviceMetadata.setProtocols(protocols);
return serviceMetadata;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"apiVersion":"2023-03-10",
"auth":["aws.auth#sigv4"],
"endpointPrefix":"smithyrpcv2protocol",
"protocol":"smithy-rpc-v2-cbor",
"protocols":["smithy-rpc-v2-cbor"],
"protocols":["smithy-rpc-v2-cbor", "json", "query"],
"serviceFullName":"RpcV2 Protocol Service",
"serviceId":"SmithyRpcV2Protocol",
"signatureVersion":"v4",
Expand Down
Loading