From e56008098f1080eca601c155d9c40c103ce26391 Mon Sep 17 00:00:00 2001 From: George Fu Date: Mon, 19 Aug 2024 17:59:37 +0000 Subject: [PATCH] subordinate ProtocolPriorityConfig to TypeScriptSettings --- README.md | 28 +- .../codegen/TypeScriptSettings.java | 61 +++- ...ority.java => ProtocolPriorityConfig.java} | 24 +- .../codegen/TypeScriptSettingsTest.java | 293 +++++++++++------- 4 files changed, 273 insertions(+), 133 deletions(-) rename smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/{ProtocolPriority.java => ProtocolPriorityConfig.java} (57%) diff --git a/README.md b/README.md index bedb33f019a..4a010e2c595 100644 --- a/README.md +++ b/README.md @@ -185,19 +185,21 @@ By default, the Smithy TypeScript code generators provide the code generation fr [`TypeScriptSettings`](smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java) contains all of the settings enabled from `smithy-build.json` and helper methods and types. The up-to-date list of top-level properties enabled for `typescript-client-codegen` can be found in `TypeScriptSettings.ArtifactType.CLIENT`. -|Setting|Required|Description| -|---|---|---| -|`package`|Yes|Name of the package in `package.json`.| -|`packageVersion`|Yes|Version of the package in `package.json`.| -|`packageDescription`|No|Description of the package in `package.json`. The default value is `${package} client`| -|`packageJson`|No|Custom `package.json` properties that will be merged with the base `package.json`. The default value is an empty object.| -|`packageManager`|No|Configured package manager for the package. The default value is `yarn`.| -|`service`|No|The Shape ID of the service to generate a client for. If not provided, the code generator will attempt to infer the service Shape ID. If there is exactly 1 service found in the model, then the service is used as the inferred Shape ID. If no services are found, then code generation fails. If more than 1 service is found, then code generation fails.| -|`protocol`|No|The Shape ID of the protocol used to generate serialization and deserialization. If not provided, the code generator will attempt to resolve the highest priority service protocol supported in code generation (registered through `TypeScriptIntegration`). If no protocols are found, code generation will use serialization and deserialization error stubs.| -|`private`|No|Whether the package is `private` in `package.json`. The default value is `false`.| -|`requiredMemberMode`|No|**NOT RECOMMENDED DUE TO BACKWARD COMPATIBILITY CONCERNS.** Sets whether members marked with the `@required` trait are allowed to be `undefined`. See more details on the risks in `TypeScriptSettings.RequiredMemberMode`. The default value is `nullable`.| -|`createDefaultReadme`|No|Whether to generate a default `README.md` for the package. The default value is `false`.| -|`useLegacyAuth`|No|**NOT RECOMMENDED, AVAILABLE ONLY FOR BACKWARD COMPATIBILITY CONCERNS.** Flag that enables using legacy auth. When in doubt, use the default identity and auth behavior (not configuring `useLegacyAuth`) as the golden path.| +| Setting |Required| Description | +|---------------------------|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `package` |Yes| Name of the package in `package.json`. | +| `packageVersion` |Yes| Version of the package in `package.json`. | +| `packageDescription` |No| Description of the package in `package.json`. The default value is `${package} client` | +| `packageJson` |No| Custom `package.json` properties that will be merged with the base `package.json`. The default value is an empty object. | +| `packageManager` |No| Configured package manager for the package. The default value is `yarn`. | +| `service` |No| The Shape ID of the service to generate a client for. If not provided, the code generator will attempt to infer the service Shape ID. If there is exactly 1 service found in the model, then the service is used as the inferred Shape ID. If no services are found, then code generation fails. If more than 1 service is found, then code generation fails. | +| `protocol` |No| The Shape ID of the protocol used to generate serialization and deserialization. If not provided, the code generator will attempt to resolve the highest priority service protocol supported in code generation (registered through `TypeScriptIntegration`). If no protocols are found, code generation will use serialization and deserialization error stubs. | +| `private` |No| Whether the package is `private` in `package.json`. The default value is `false`. | +| `requiredMemberMode` |No| **NOT RECOMMENDED DUE TO BACKWARD COMPATIBILITY CONCERNS.** Sets whether members marked with the `@required` trait are allowed to be `undefined`. See more details on the risks in `TypeScriptSettings.RequiredMemberMode`. The default value is `nullable`. | +| `createDefaultReadme` |No| Whether to generate a default `README.md` for the package. The default value is `false`. | +| `useLegacyAuth` |No| **NOT RECOMMENDED, AVAILABLE ONLY FOR BACKWARD COMPATIBILITY CONCERNS.** Flag that enables using legacy auth. When in doubt, use the default identity and auth behavior (not configuring `useLegacyAuth`) as the golden path. | +| `serviceProrotolPriority` |No| Map of service `ShapeId` strings to lists of protocol `ShapeId` strings. Used to override protocol selection behavior. | +| `defaultProtocolPriority` |No| List of protocol `ShapeId` strings. Lower precedence than `serviceProrotolPriority` but applies to all services. | #### `typescript-client-codegen` plugin artifacts diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java index c82cab8ba76..dc95b9b8253 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; import java.util.logging.Logger; @@ -28,6 +29,7 @@ import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.knowledge.ServiceIndex; +import software.amazon.smithy.model.node.ArrayNode; import software.amazon.smithy.model.node.BooleanNode; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; @@ -37,7 +39,7 @@ import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.DefaultTrait; import software.amazon.smithy.model.traits.RequiredTrait; -import software.amazon.smithy.typescript.codegen.protocols.ProtocolPriority; +import software.amazon.smithy.typescript.codegen.protocols.ProtocolPriorityConfig; import software.amazon.smithy.utils.SmithyUnstableApi; /** @@ -61,6 +63,8 @@ public final class TypeScriptSettings { private static final String CREATE_DEFAULT_README = "createDefaultReadme"; private static final String USE_LEGACY_AUTH = "useLegacyAuth"; private static final String GENERATE_TYPEDOC = "generateTypeDoc"; + private static final String SERVICE_PROTOCOL_PRIORITY = "serviceProtocolPriority"; + private static final String DEFAULT_PROTOCOL_PRIORITY = "defaultProtocolPriority"; private String packageName; private String packageDescription = ""; @@ -79,6 +83,7 @@ public final class TypeScriptSettings { private boolean createDefaultReadme = false; private boolean useLegacyAuth = false; private boolean generateTypeDoc = false; + private final ProtocolPriorityConfig protocolPriorityConfig = new ProtocolPriorityConfig(); @Deprecated public static TypeScriptSettings from(Model model, ObjectNode config) { @@ -130,6 +135,9 @@ public static TypeScriptSettings from(Model model, ObjectNode config, ArtifactTy .orElse(RequiredMemberMode.NULLABLE)); settings.setPluginSettings(config); + + settings.readProtocolPriorityConfiguration(config); + return settings; } @@ -452,7 +460,7 @@ public ShapeId resolveServiceProtocol(Model model, ServiceShape service, Set protocolPriority = ProtocolPriority.getProtocolPriority(service.toShapeId()); + List protocolPriority = this.protocolPriorityConfig.getProtocolPriority(service.toShapeId()); List protocolPriorityList = protocolPriority != null && !protocolPriority.isEmpty() ? protocolPriority : new ArrayList<>(supportedProtocols); @@ -489,6 +497,13 @@ public String getDefaultSigningName() { return defaultSigningName; } + /** + * @return config container for service and/or default protocol selection priority overrides. + */ + public ProtocolPriorityConfig getProtocolPriority() { + return protocolPriorityConfig; + } + /** * An enum indicating the type of artifact the code generator will produce. */ @@ -593,4 +608,46 @@ public static PackageManager fromString(String s) { throw new CodegenException(String.format("Unsupported package manager: %s", s)); } } + + /** + * Reads serviceProtocolPriority and defaultProtocolPriority configuration fields. + * { + * serviceProtocolPriority: { + * "namespace#Service": ["namespace#Protocol1", "namespace#Protocol2"] + * }, + * defaultProtocolPriority: ["namespace#Protocol"] + * } + */ + private void readProtocolPriorityConfiguration(ObjectNode config) { + try { + Optional protocolPriorityNode = config.getObjectMember(SERVICE_PROTOCOL_PRIORITY); + if (protocolPriorityNode.isPresent()) { + ObjectNode objectNode = protocolPriorityNode.get(); + objectNode.getMembers().forEach((StringNode k, Node v) -> { + ShapeId serviceShapeId = ShapeId.from(k.getValue()); + List protocolList = v.asArrayNode().get().getElementsAs( + e -> ShapeId.from(e.asStringNode().get().getValue()) + ); + protocolPriorityConfig.setProtocolPriority( + serviceShapeId, + protocolList + ); + }); + } + Optional defaultProtocolPriorityOpt = config.getArrayMember(DEFAULT_PROTOCOL_PRIORITY); + if (defaultProtocolPriorityOpt.isPresent()) { + ArrayNode defaultProtocolPriorityStringArr = defaultProtocolPriorityOpt.get(); + protocolPriorityConfig.setCustomDefaultProtocolPriority( + defaultProtocolPriorityStringArr.getElementsAs( + e -> ShapeId.from(e.asStringNode().get().getValue()) + ) + ); + } + } catch (Exception e) { + throw new IllegalArgumentException( + "Error while parsing serviceProtocolPriority or defaultProtocolPriority configuration fields", + e + ); + } + } } diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/ProtocolPriority.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/ProtocolPriorityConfig.java similarity index 57% rename from smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/ProtocolPriority.java rename to smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/ProtocolPriorityConfig.java index 89cdf55f815..ff1f819307f 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/ProtocolPriority.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/ProtocolPriorityConfig.java @@ -15,24 +15,22 @@ /** * Allows customization of protocol selection for specific services or a global default ordering. */ -public final class ProtocolPriority { - private static final Map> SERVICE_PROTOCOL_PRIORITY_CUSTOMIZATIONS = new HashMap<>(); - private static List customDefaultPriority = null; - - private ProtocolPriority() {} +public final class ProtocolPriorityConfig { + private final Map> serviceProtocolPriorityCustomizations = new HashMap<>(); + private List customDefaultPriority = null; /** * @param serviceShapeId - service scope. * @param protocolPriorityOrder - priority order of protocols. */ - public static void setProtocolPriority(ShapeId serviceShapeId, List protocolPriorityOrder) { - SERVICE_PROTOCOL_PRIORITY_CUSTOMIZATIONS.put(serviceShapeId, protocolPriorityOrder); + public void setProtocolPriority(ShapeId serviceShapeId, List protocolPriorityOrder) { + serviceProtocolPriorityCustomizations.put(serviceShapeId, protocolPriorityOrder); } /** * @param defaultProtocolPriorityOrder - use for all services that don't have a more specific priority order. */ - public static void setCustomDefaultProtocolPriority(List defaultProtocolPriorityOrder) { + public void setCustomDefaultProtocolPriority(List defaultProtocolPriorityOrder) { customDefaultPriority = new ArrayList<>(defaultProtocolPriorityOrder); } @@ -40,8 +38,8 @@ public static void setCustomDefaultProtocolPriority(List defaultProtoco * @param serviceShapeId - service scope. * @return priority order of protocols or null if no override exists. */ - public static List getProtocolPriority(ShapeId serviceShapeId) { - return SERVICE_PROTOCOL_PRIORITY_CUSTOMIZATIONS.getOrDefault( + public List getProtocolPriority(ShapeId serviceShapeId) { + return serviceProtocolPriorityCustomizations.getOrDefault( serviceShapeId, customDefaultPriority != null ? new ArrayList<>(customDefaultPriority) : null ); @@ -50,14 +48,14 @@ public static List getProtocolPriority(ShapeId serviceShapeId) { /** * @param serviceShapeId - to unset. */ - public static void deleteProtocolPriority(ShapeId serviceShapeId) { - SERVICE_PROTOCOL_PRIORITY_CUSTOMIZATIONS.remove(serviceShapeId); + public void deleteProtocolPriority(ShapeId serviceShapeId) { + serviceProtocolPriorityCustomizations.remove(serviceShapeId); } /** * Unset the custom default priority order. */ - public static void deleteCustomDefaultProtocolPriority() { + public void deleteCustomDefaultProtocolPriority() { customDefaultPriority = null; } } diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/TypeScriptSettingsTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/TypeScriptSettingsTest.java index 272122b54b9..fb52ff75739 100644 --- a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/TypeScriptSettingsTest.java +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/TypeScriptSettingsTest.java @@ -16,7 +16,6 @@ import software.amazon.smithy.model.node.ObjectNode; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.ShapeId; -import software.amazon.smithy.typescript.codegen.protocols.ProtocolPriority; import software.amazon.smithy.utils.MapUtils; import static org.hamcrest.MatcherAssert.assertThat; @@ -27,6 +26,19 @@ @ExtendWith(MockitoExtension.class) public class TypeScriptSettingsTest { + // these are mock protocol names. + ShapeId rpcv2Cbor = ShapeId.from("namespace#rpcv2Cbor"); + ShapeId json1_0 = ShapeId.from("namespace#json1_0"); + ShapeId json1_1 = ShapeId.from("namespace#json1_1"); + ShapeId restJson1 = ShapeId.from("namespace#restJson1"); + ShapeId restXml = ShapeId.from("namespace#restXml"); + ShapeId query = ShapeId.from("namespace#query"); + ShapeId serviceQuery = ShapeId.from("namespace#serviceQuery"); + LinkedHashSet protocolShapeIds = new LinkedHashSet<>( + List.of( + json1_0, json1_1, restJson1, rpcv2Cbor, restXml, query, serviceQuery + ) + ); @Test public void resolvesDefaultService() { @@ -99,118 +111,189 @@ private static Stream providePackageDescriptionTestCases() { } @Test - public void resolveServiceProtocol(@Mock Model model, - @Mock ServiceShape service, - @Mock ServiceIndex serviceIndex) { + public void resolveServiceProtocolSelectJson(@Mock Model model, + @Mock ServiceShape service, + @Mock ServiceIndex serviceIndex) { TypeScriptSettings subject = new TypeScriptSettings(); + when(model.getKnowledge(any(), any())).thenReturn(serviceIndex); + ShapeId serviceShapeId = ShapeId.from("namespace#Service"); + when(service.toShapeId()).thenReturn(serviceShapeId); - // these are mock protocol names. - ShapeId rpcv2Cbor = ShapeId.from("namespace#rpcv2Cbor"); - ShapeId json1_0 = ShapeId.from("namespace#json1_0"); - ShapeId json1_1 = ShapeId.from("namespace#json1_1"); - ShapeId restJson1 = ShapeId.from("namespace#restJson1"); - ShapeId restXml = ShapeId.from("namespace#restXml"); - ShapeId query = ShapeId.from("namespace#query"); - ShapeId serviceQuery = ShapeId.from("namespace#serviceQuery"); + // spec case 1. + when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( + rpcv2Cbor, null, + json1_0, null + )); + ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); + // JS customization has JSON at higher default priority than CBOR. + assertEquals(json1_0, protocol); + } + @Test + public void resolveServiceProtocolSelectOnlyOption(@Mock Model model, + @Mock ServiceShape service, + @Mock ServiceIndex serviceIndex) { + TypeScriptSettings subject = new TypeScriptSettings(); when(model.getKnowledge(any(), any())).thenReturn(serviceIndex); ShapeId serviceShapeId = ShapeId.from("namespace#Service"); when(service.toShapeId()).thenReturn(serviceShapeId); - LinkedHashSet protocolShapeIds = new LinkedHashSet<>( - List.of( - json1_0, json1_1, restJson1, rpcv2Cbor, restXml, query, serviceQuery - ) - ); + // spec case 2. + when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( + rpcv2Cbor, null + )); + ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); + assertEquals(rpcv2Cbor, protocol); + } - { - // spec case 1. - when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( - rpcv2Cbor, null, - json1_0, null - )); - ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); - // JS customization has JSON at higher default priority than CBOR. - assertEquals(json1_0, protocol); - } - - { - // spec case 2. - when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( - rpcv2Cbor, null - )); - ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); - assertEquals(rpcv2Cbor, protocol); - } - - { - // spec case 3. - when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( - rpcv2Cbor, null, - json1_0, null, - query, null - )); - ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); - // JS customization has JSON at higher default priority than CBOR. - assertEquals(json1_0, protocol); - } - - { - // spec case 4. - when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( - json1_0, null, - query, null - )); - ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); - assertEquals(json1_0, protocol); - } - - { - // spec case 5. - when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( - query, null - )); - ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); - assertEquals(query, protocol); - } - - { - // service override, non-spec - when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( - json1_0, null, - json1_1, null, - restJson1, null, - rpcv2Cbor, null, - restXml, null, - query, null, - serviceQuery, null - )); - ProtocolPriority.setProtocolPriority(serviceShapeId, List.of( - serviceQuery, rpcv2Cbor, json1_1, restJson1, restXml, query - )); - ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); - ProtocolPriority.deleteProtocolPriority(serviceShapeId); - assertEquals(serviceQuery, protocol); - } - - { - // global default override - when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( - json1_0, null, - json1_1, null, - restJson1, null, - rpcv2Cbor, null, - restXml, null, - query, null, - serviceQuery, null - )); - ProtocolPriority.setCustomDefaultProtocolPriority(List.of( - rpcv2Cbor, json1_1, restJson1, restXml, query - )); - ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); - ProtocolPriority.deleteCustomDefaultProtocolPriority(); - assertEquals(rpcv2Cbor, protocol); - } + @Test + public void resolveServiceProtocolSelectJsonOverQueryAndCbor(@Mock Model model, + @Mock ServiceShape service, + @Mock ServiceIndex serviceIndex) { + TypeScriptSettings subject = new TypeScriptSettings(); + when(model.getKnowledge(any(), any())).thenReturn(serviceIndex); + ShapeId serviceShapeId = ShapeId.from("namespace#Service"); + when(service.toShapeId()).thenReturn(serviceShapeId); + + // spec case 3. + when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( + rpcv2Cbor, null, + json1_0, null, + query, null + )); + ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); + // JS customization has JSON at higher default priority than CBOR. + assertEquals(json1_0, protocol); + } + + @Test + public void resolveServiceProtocolSelectJsonOverQuery(@Mock Model model, + @Mock ServiceShape service, + @Mock ServiceIndex serviceIndex) { + TypeScriptSettings subject = new TypeScriptSettings(); + when(model.getKnowledge(any(), any())).thenReturn(serviceIndex); + ShapeId serviceShapeId = ShapeId.from("namespace#Service"); + when(service.toShapeId()).thenReturn(serviceShapeId); + + // spec case 4. + when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( + json1_0, null, + query, null + )); + ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); + assertEquals(json1_0, protocol); + } + + @Test + public void resolveServiceProtocolSelectQueryWhenSingularOption(@Mock Model model, + @Mock ServiceShape service, + @Mock ServiceIndex serviceIndex) { + TypeScriptSettings subject = new TypeScriptSettings(); + when(model.getKnowledge(any(), any())).thenReturn(serviceIndex); + ShapeId serviceShapeId = ShapeId.from("namespace#Service"); + when(service.toShapeId()).thenReturn(serviceShapeId); + + // spec case 5. + when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( + query, null + )); + ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); + assertEquals(query, protocol); + } + + @Test + public void resolveServiceProtocolSelectServiceCustomPriority(@Mock Model model, + @Mock ServiceShape service, + @Mock ServiceIndex serviceIndex) { + TypeScriptSettings subject = new TypeScriptSettings(); + when(model.getKnowledge(any(), any())).thenReturn(serviceIndex); + ShapeId serviceShapeId = ShapeId.from("namespace#Service"); + when(service.toShapeId()).thenReturn(serviceShapeId); + + // service override, non-spec + when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( + json1_0, null, + json1_1, null, + restJson1, null, + rpcv2Cbor, null, + restXml, null, + query, null, + serviceQuery, null + )); + subject.getProtocolPriority().setProtocolPriority(serviceShapeId, List.of( + serviceQuery, rpcv2Cbor, json1_1, restJson1, restXml, query + )); + ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); + subject.getProtocolPriority().deleteProtocolPriority(serviceShapeId); + assertEquals(serviceQuery, protocol); + } + + @Test + public void resolveServiceProtocolSelectDefaultCustomPriority(@Mock Model model, + @Mock ServiceShape service, + @Mock ServiceIndex serviceIndex) { + TypeScriptSettings subject = new TypeScriptSettings(); + when(model.getKnowledge(any(), any())).thenReturn(serviceIndex); + ShapeId serviceShapeId = ShapeId.from("namespace#Service"); + when(service.toShapeId()).thenReturn(serviceShapeId); + + // global default override + when(serviceIndex.getProtocols(service)).thenReturn(MapUtils.of( + json1_0, null, + json1_1, null, + restJson1, null, + rpcv2Cbor, null, + restXml, null, + query, null, + serviceQuery, null + )); + subject.getProtocolPriority().setCustomDefaultProtocolPriority(List.of( + rpcv2Cbor, json1_1, restJson1, restXml, query + )); + ShapeId protocol = subject.resolveServiceProtocol(model, service, protocolShapeIds); + subject.getProtocolPriority().deleteCustomDefaultProtocolPriority(); + assertEquals(rpcv2Cbor, protocol); + } + + @Test + public void parseProtocolPriorityJson() { + Model model = Model.assembler() + .addImport(getClass().getResource("simple-service.smithy")) + .assemble() + .unwrap(); + + ObjectNode settings = Node.objectNodeBuilder() + .withMember("service", Node.from("smithy.example#Example")) + .withMember("package", Node.from("example")) + .withMember("packageVersion", Node.from("1.0.0")) + .withMember("serviceProtocolPriority", Node.parse( + """ + { + "namespace#Service1": ["namespace#Protocol1", "namespace#Protocol2"], + "namespace#Service2": ["namespace#Protocol2", "namespace#Protocol1"] + } + """)) + .withMember("defaultProtocolPriority", Node.parse( + """ + ["namespace#Protocol3", "namespace#Protocol4"] + """ + )) + .build(); + + final TypeScriptSettings subject = TypeScriptSettings.from(model, settings); + + assertEquals( + ShapeId.from("namespace#Protocol2"), + subject.getProtocolPriority().getProtocolPriority(ShapeId.from("namespace#Service1")).get(1) + ); + assertEquals( + ShapeId.from("namespace#Protocol2"), + subject.getProtocolPriority().getProtocolPriority(ShapeId.from("namespace#Service2")).get(0) + ); + assertEquals( + ShapeId.from("namespace#Protocol4"), + subject.getProtocolPriority().getProtocolPriority(ShapeId.from("namespace#Service5")).get(1) + ); } @Test