diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java index cde70274b81..6079e98c107 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java @@ -112,6 +112,9 @@ final class IdlModelParser extends SimpleParser { private final String filename; private TraitEntry pendingDocumentationComment; + private String operationInputSuffix = "Input"; + private String operationOutputSuffix = "Output"; + // A pending trait that also doesn't yet have a resolved trait shape ID. static final class TraitEntry { final String traitName; @@ -181,6 +184,7 @@ ModelSyntaxException syntax(ShapeId shapeId, String message) { } private void parseControlSection() { + Set definedKeys = new HashSet<>(); while (peek() == '$') { expect('$'); ws(); @@ -189,15 +193,19 @@ private void parseControlSection() { expect(':'); ws(); - // Validation here for better error location. - if (key.equals("version") && modelFile.getVersion() != Version.UNKNOWN) { - throw syntax("Cannot define multiple versions in the same file"); + if (definedKeys.contains(key)) { + throw syntax(format("Duplicate control statement `%s`", key)); } + definedKeys.add(key); Node value = IdlNodeParser.parseNode(this); if (key.equals("version")) { onVersion(value); + } else if (key.equals("operationInputSuffix")) { + operationInputSuffix = value.expectStringNode().getValue(); + } else if (key.equals("operationOutputSuffix")) { + operationOutputSuffix = value.expectStringNode().getValue(); } else { modelFile.events().add(ValidationEvent.builder() .id(Validator.MODEL_ERROR) @@ -519,6 +527,12 @@ private void parseMember( ws(); expect(':'); + + if (peek() == '=') { + throw syntax("Defining structures inline with the `:=` syntax may only be used when " + + "defining operation input and output shapes."); + } + ws(); ShapeId memberId = parent.withMember(memberName); MemberShape.Builder memberBuilder = MemberShape.builder().id(memberId).source(memberLocation); @@ -570,37 +584,119 @@ private void parseStructuredShape( ws(); // Parse optional "with" statements to add mixins, but only if it's supported by the version. - if (peek() == 'w') { - expect('w'); - expect('i'); - expect('t'); - expect('h'); + parseMixins(id); + parseMembers(id, Collections.emptySet(), structureMember); + } - if (!modelFile.getVersion().supportsMixins()) { - throw syntax(id, "Mixins can only be used with Smithy version 2 or later. " - + "Attempted to use mixins with version `" + modelFile.getVersion() + "`."); - } + private void parseMixins(ShapeId id) { + if (peek() != 'w') { + return; + } - ws(); - do { - String target = ParserUtils.parseShapeId(this); - modelFile.addForwardReference(target, resolved -> modelFile.addPendingMixin(id, resolved)); - ws(); - } while (peek() != '{'); + expect('w'); + expect('i'); + expect('t'); + expect('h'); + + if (!modelFile.getVersion().supportsMixins()) { + throw syntax(id, "Mixins can only be used with Smithy version 2 or later. " + + "Attempted to use mixins with version `" + modelFile.getVersion() + "`."); } - parseMembers(id, Collections.emptySet(), structureMember); + ws(); + do { + String target = ParserUtils.parseShapeId(this); + modelFile.addForwardReference(target, resolved -> modelFile.addPendingMixin(id, resolved)); + ws(); + } while (peek() != '{'); } private void parseOperationStatement(ShapeId id, SourceLocation location) { - ws(); OperationShape.Builder builder = OperationShape.builder().id(id).source(location); - ObjectNode node = IdlNodeParser.parseObjectNode(this, id.toString()); - LoaderUtils.checkForAdditionalProperties(node, id, OPERATION_PROPERTY_NAMES, modelFile.events()); + parseProperties(id, propertyName -> { + switch (propertyName) { + case "input": + parseInlineableOperationMember(id, operationInputSuffix, builder::input); + break; + case "output": + parseInlineableOperationMember(id, operationOutputSuffix, builder::output); + break; + case "errors": + parseIdList(builder::addError); + break; + default: + throw syntax(id, String.format("Unknown property %s for %s", propertyName, id)); + } + }); + modelFile.onShape(builder); + } + + private void parseProperties(ShapeId id, Consumer valueParser) { + ws(); + expect('{'); + ws(); + + Set defined = new HashSet<>(); + while (!eof() && peek() != '}') { + String key = IdlNodeParser.parseNodeObjectKey(this); + if (defined.contains(key)) { + throw syntax(id, String.format("Duplicate %s binding for %s", key, id)); + } + defined.add(key); + + ws(); + expect(':'); + valueParser.accept(key); + ws(); + } + + expect('}'); + } + + private void parseInlineableOperationMember(ShapeId id, String suffix, Consumer consumer) { + if (peek() == '=') { + if (!modelFile.getVersion().supportsInlineOperationIO()) { + throw syntax(id, "Inlined operation inputs and outputs can only be used with Smithy version 2 or " + + "later. Attempted to use inlined IO with version `" + modelFile.getVersion() + "`."); + } + expect('='); + clearPendingDocs(); + ws(); + consumer.accept(parseInlineStructure(id.getName() + suffix)); + } else { + ws(); + modelFile.addForwardReference(ParserUtils.parseShapeId(this), consumer); + } + } + + private ShapeId parseInlineStructure(String name) { + List traits = parseDocsAndTraits(); + ShapeId id = ShapeId.fromRelative(modelFile.namespace(), name); + SourceLocation location = currentLocation(); + parseMixins(id); + StructureShape.Builder builder = StructureShape.builder().id(id).source(location); + modelFile.onShape(builder); - optionalId(node, "input", builder::input); - optionalId(node, "output", builder::output); - optionalIdList(node, ERRORS_KEYS, builder::addError); + parseMembers(id, Collections.emptySet(), true); + addTraits(id, traits); + clearPendingDocs(); + ws(); + return id; + } + + private void parseIdList(Consumer consumer) { + increaseNestingLevel(); + ws(); + expect('['); + ws(); + + while (!eof() && peek() != ']') { + modelFile.addForwardReference(ParserUtils.parseShapeId(this), consumer); + ws(); + } + + expect(']'); + decreaseNestingLevel(); } private void parseServiceStatement(ShapeId id, SourceLocation location) { diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlNodeParser.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlNodeParser.java index 3794161955e..8645c15cd88 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlNodeParser.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlNodeParser.java @@ -135,6 +135,10 @@ static ObjectNode parseObjectNode(IdlModelParser parser, String parent) { String key = parseNodeObjectKey(parser); parser.ws(); parser.expect(':'); + if (parser.peek() == '=') { + throw parser.syntax("The `:=` syntax may only be used when defining inline operation input and " + + "output shapes."); + } parser.ws(); Node value = parseNode(parser); StringNode keyNode = new StringNode(key, keyLocation); diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/Version.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/Version.java index 40588ec23cd..afb33b2bb3c 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/Version.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/Version.java @@ -82,6 +82,15 @@ boolean supportsRequiredSugar() { return this == VERSION_1_1 || this == VERSION_2_0; } + /** + * Checks if this version of the IDL supports inlined operation IO shapes. + * + * @return Returns true if this version supports inlined operation IO shapes. + */ + boolean supportsInlineOperationIO() { + return this == VERSION_1_1 || this == VERSION_2_0; + } + /** * Perform version-specific trait validation. * diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java index dec85fd40a3..bf9d6fd1d57 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java @@ -145,15 +145,39 @@ private String serialize(Model fullModel, Collection shapes) { SmithyCodeWriter codeWriter = new SmithyCodeWriter(namespace, fullModel); NodeSerializer nodeSerializer = new NodeSerializer(codeWriter, fullModel); - ShapeSerializer shapeSerializer = new ShapeSerializer(codeWriter, nodeSerializer, traitFilter, fullModel); + Set inlineableShapes = getInlineableShapes(fullModel, shapes); + ShapeSerializer shapeSerializer = new ShapeSerializer( + codeWriter, nodeSerializer, traitFilter, fullModel, inlineableShapes); shapes.stream() .filter(FunctionalUtils.not(Shape::isMemberShape)) + .filter(shape -> !inlineableShapes.contains(shape.getId())) .sorted(new ShapeComparator()) .forEach(shape -> shape.accept(shapeSerializer)); return serializeHeader(fullModel, namespace) + codeWriter.toString(); } + private Set getInlineableShapes(Model fullModel, Collection shapes) { + Set inlineableShapes = new HashSet<>(); + for (Shape shape : shapes) { + if (!shape.isOperationShape()) { + continue; + } + OperationShape operation = shape.asOperationShape().get(); + operation.getInput().ifPresent(shapeId -> { + if (shapes.contains(fullModel.expectShape(shapeId))) { + inlineableShapes.add(shapeId); + } + }); + operation.getOutput().ifPresent(shapeId -> { + if (shapes.contains(fullModel.expectShape(shapeId))) { + inlineableShapes.add(shapeId); + } + }); + } + return inlineableShapes; + } + private String serializeHeader(Model fullModel, String namespace) { SmithyCodeWriter codeWriter = new SmithyCodeWriter(null, fullModel); NodeSerializer nodeSerializer = new NodeSerializer(codeWriter, fullModel); @@ -342,17 +366,20 @@ private static final class ShapeSerializer extends ShapeVisitor.Default { private final NodeSerializer nodeSerializer; private final Predicate traitFilter; private final Model model; + private final Set inlineableShapes; ShapeSerializer( SmithyCodeWriter codeWriter, NodeSerializer nodeSerializer, Predicate traitFilter, - Model model + Model model, + Set inlineableShapes ) { this.codeWriter = codeWriter; this.nodeSerializer = nodeSerializer; this.traitFilter = traitFilter; this.model = model; + this.inlineableShapes = inlineableShapes; } @Override @@ -380,6 +407,13 @@ private void shapeWithMembers(Shape shape, List members, boolean st serializeTraits(shape.getIntroducedTraits()); codeWriter.writeInline("$L $L ", shape.getType(), shape.getId().getName()); + writeMixins(shape, !nonMixinMembers.isEmpty()); + writeShapeMembers(nonMixinMembers, structureMember); + codeWriter.write(""); + applyIntroducedTraits(mixinMembers); + } + + private void writeMixins(Shape shape, boolean hasNonMixinMembers) { if (shape.getMixins().size() == 1) { codeWriter.writeInline("with $I ", shape.getMixins().iterator().next()); } else if (shape.getMixins().size() > 1) { @@ -388,39 +422,41 @@ private void shapeWithMembers(Shape shape, List members, boolean st // Trailing spaces are trimmed. codeWriter.writeInline("\n $I ", id); } - if (!nonMixinMembers.isEmpty()) { + if (hasNonMixinMembers) { codeWriter.write(""); } } + } - if (nonMixinMembers.isEmpty()) { + private void writeShapeMembers(Collection members, boolean isStructure) { + if (members.isEmpty()) { // When the are no members to write, put "{}" on the same line. - codeWriter.writeInline("{}").write("").write(""); + codeWriter.writeInline("{}").write(""); } else { codeWriter.openBlock("{", "}", () -> { - for (MemberShape member : nonMixinMembers) { + for (MemberShape member : members) { // Omit required traits and instead use "!" syntax. serializeTraits(member.getAllTraits(), TraitFeature.OMIT_REQUIRED); // Suffix with "!" if it's a required structure member. - String requiredSuffix = structureMember && member.isRequired() ? "!" : ""; + String requiredSuffix = isStructure && member.isRequired() ? "!" : ""; codeWriter.write("$L: $I$L", member.getMemberName(), member.getTarget(), requiredSuffix); } - }).write(""); - - if (!mixinMembers.isEmpty()) { - for (MemberShape member : mixinMembers) { - // Use short form for a single trait, and block form for multiple traits. - if (member.getIntroducedTraits().size() == 1) { - codeWriter.writeInline("apply $I ", member.getId()); - serializeTraits(member.getIntroducedTraits(), TraitFeature.NO_SPECIAL_DOCS_SYNTAX); - codeWriter.write(""); - } else { - codeWriter.openBlock("apply $I {", "}", member.getId(), () -> { - // Only serialize local traits, and don't use special documentation syntax here. - serializeTraits(member.getIntroducedTraits(), TraitFeature.NO_SPECIAL_DOCS_SYNTAX); - }).write(""); - } - } + }); + } + } + + private void applyIntroducedTraits(Collection members) { + for (MemberShape member : members) { + // Use short form for a single trait, and block form for multiple traits. + if (member.getIntroducedTraits().size() == 1) { + codeWriter.writeInline("apply $I ", member.getId()); + serializeTraits(member.getIntroducedTraits(), TraitFeature.NO_SPECIAL_DOCS_SYNTAX); + codeWriter.write(""); + } else if (!member.getIntroducedTraits().isEmpty()) { + codeWriter.openBlock("apply $I {", "}", member.getId(), () -> { + // Only serialize local traits, and don't use special documentation syntax here. + serializeTraits(member.getIntroducedTraits(), TraitFeature.NO_SPECIAL_DOCS_SYNTAX); + }).write(""); } } } @@ -570,17 +606,58 @@ public Void operationShape(OperationShape shape) { } codeWriter.openBlock("operation $L {", shape.getId().getName()); - shape.getInput().ifPresent(shapeId -> codeWriter.write("input: $I", shapeId)); - shape.getOutput().ifPresent(shapeId -> codeWriter.write("output: $I", shapeId)); + List mixinMembers = new ArrayList<>(); + shape.getInput().ifPresent(shapeId -> { + mixinMembers.addAll(writeInlineableProperty("input", shapeId, shape.getId().getName() + "Input")); + }); + shape.getOutput().ifPresent(shapeId -> { + mixinMembers.addAll(writeInlineableProperty("output", shapeId, shape.getId().getName() + "Output")); + }); codeWriter.writeOptionalIdList("errors", shape.getErrors()); codeWriter.closeBlock("}"); codeWriter.write(""); + applyIntroducedTraits(mixinMembers); return null; } private boolean isEmptyOperation(OperationShape shape) { return !(shape.getInput().isPresent() || shape.getOutput().isPresent() || !shape.getErrors().isEmpty()); } + + private Collection writeInlineableProperty(String key, ShapeId shapeId, String defaultName) { + if (!inlineableShapes.contains(shapeId)) { + codeWriter.write("$L: $I", key, shapeId); + return Collections.emptyList(); + } + + StructureShape structure = model.expectShape(shapeId, StructureShape.class); + if (structure.getAllTraits().isEmpty()) { + codeWriter.writeInline("$L := ", key); + } else { + codeWriter.write("$L := ", key); + codeWriter.indent(); + serializeTraits(structure); + } + + List nonMixinMembers = new ArrayList<>(); + List mixinMembers = new ArrayList<>(); + for (MemberShape member : structure.members()) { + if (member.getMixins().isEmpty()) { + nonMixinMembers.add(member); + } else if (!member.getIntroducedTraits().isEmpty()) { + mixinMembers.add(member); + } + } + + writeMixins(structure, !nonMixinMembers.isEmpty()); + writeShapeMembers(nonMixinMembers, true); + + if (!structure.getAllTraits().isEmpty()) { + codeWriter.dedent(); + } + + return mixinMembers; + } } /** diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializerTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializerTest.java index dad1ce73799..480ae2d6ca6 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializerTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializerTest.java @@ -45,7 +45,7 @@ public void testConversion(Path path) { } String serializedString = serialized.entrySet().iterator().next().getValue(); - Assertions.assertEquals(serializedString, IoUtils.readUtf8File(path).replaceAll("\\R", "\n")); + Assertions.assertEquals(IoUtils.readUtf8File(path).replaceAll("\\R", "\n"), serializedString); } @Test diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/dupe-operation-binding.errors b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/dupe-operation-binding.errors index 3d96b3f911f..7ef9d09a4e3 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/dupe-operation-binding.errors +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/dupe-operation-binding.errors @@ -1 +1 @@ -[ERROR] -: Parse error at line 5, column 23 near `, | Model +[ERROR] com.foo#GetFoo: Parse error at line 5, column 10 near `: `: Duplicate input binding for com.foo#GetFoo | Model diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/excess-operation-keys.errors b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/excess-operation-keys.errors new file mode 100644 index 00000000000..4e0e1f1cc1d --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/excess-operation-keys.errors @@ -0,0 +1 @@ +[ERROR] com.example#Operation: Parse error at line 4, column 25 near ` String`: Unknown property invalidOperationKey for com.example#Operation | Model diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/excess-operation-keys.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/excess-operation-keys.smithy new file mode 100644 index 00000000000..643da368857 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/excess-operation-keys.smithy @@ -0,0 +1,5 @@ +namespace com.example + +operation Operation { + invalidOperationKey: String +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/control-version-defined-twice.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/control-version-defined-twice.smithy index 228b63a082e..ab384ee6c60 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/control-version-defined-twice.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/control-version-defined-twice.smithy @@ -1,4 +1,4 @@ -// Parse error at line 3, column 11 near `"1`: Cannot define multiple versions in the same file +// Parse error at line 3, column 11 near `"1`: Duplicate control statement `version` $version: "1.0" $version: "1.0" namespace com.baz diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-error.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-error.smithy new file mode 100644 index 00000000000..bd49f6fd677 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-error.smithy @@ -0,0 +1,7 @@ +// Expected: '[', but found '=' +$version: "2.0" +namespace smithy.example + +operation MyOperation { + errors := [{}] +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-io-in-explicit-1-0.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-io-in-explicit-1-0.smithy new file mode 100644 index 00000000000..7f0eff66eaf --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-io-in-explicit-1-0.smithy @@ -0,0 +1,7 @@ +// Inlined operation inputs and outputs can only be used with Smithy version 2 or later. Attempted to use inlined IO with version `1.0`. +$version: "1.0" +namespace smithy.example + +operation MyOperation { + input := {} +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-io-in-implicit-1-0.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-io-in-implicit-1-0.smithy new file mode 100644 index 00000000000..3f1e8c2c9dc --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-io-in-implicit-1-0.smithy @@ -0,0 +1,6 @@ +// Inlined operation inputs and outputs can only be used with Smithy version 2 or later. Attempted to use inlined IO with version ``. +namespace smithy.example + +operation MyOperation { + input := {} +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-name-override.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-name-override.smithy new file mode 100644 index 00000000000..cf09aec7143 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-name-override.smithy @@ -0,0 +1,7 @@ +// Parse error at line 6, column 14 near `CustomName`: Expected: '{', but found 'C' +$version: "2.0" +namespace smithy.example + +operation MyOperation { + input := CustomName {} +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-single-error.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-single-error.smithy new file mode 100644 index 00000000000..f6cdc6769f0 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-single-error.smithy @@ -0,0 +1,9 @@ +// Parse error at line 6, column 12 near `= ` +$version: "2.0" +namespace smithy.example + +operation MyOperation { + errors = [ + := {} + ] +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-structure-member.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-structure-member.smithy new file mode 100644 index 00000000000..7f83ad69c79 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/inline-structure-member.smithy @@ -0,0 +1,7 @@ +// Defining structures inline with the `:=` syntax may only be used when defining operation input and output shapes. +$version: "2.0" +namespace smithy.example + +structure Structure { + inlined := String +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/walrus-operator-in-node.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/walrus-operator-in-node.smithy new file mode 100644 index 00000000000..1ab74a1009a --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/inline-io/walrus-operator-in-node.smithy @@ -0,0 +1,8 @@ +// The `:=` syntax may only be used when defining inline operation input and output shapes. +$version: "2.0" + +metadata foo = { + inlined := {} +} + +namespace smithy.example diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.json index e2173cb30b8..aa95b56c725 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.json +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.json @@ -40,6 +40,24 @@ "smithy.api#documentation": "The documentation\nof this trait!", "smithy.api#trait": {} } + }, + "smithy.example#MyOperation": { + "type": "operation", + "input": { + "target": "smithy.example#MyOperationInput" + }, + "output": { + "target": "smithy.example#MyOperationOutput" + } + }, + "smithy.example#MyOperationInput": { + "type": "structure" + }, + "smithy.example#MyOperationOutput": { + "type": "structure", + "traits": { + "smithy.api#documentation": "These are not ignored because they come AFTER the walrus\noperator." + } } } } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.smithy index ae06c6b8f67..0c21f8518b4 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.smithy @@ -1,3 +1,4 @@ +$version: "2.0" /// This comment is ignored. namespace smithy.example @@ -33,4 +34,16 @@ structure MyTrait { /// more... } +operation MyOperation { + input + /// These docs are ignored because they come BEFORE the walrus + /// operator. + := {} + + output := + /// These are not ignored because they come AFTER the walrus + /// operator. + {} +} + /// This is treated as a comment. diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/custom-suffix.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/custom-suffix.json new file mode 100644 index 00000000000..c34aa06d7e1 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/custom-suffix.json @@ -0,0 +1,20 @@ +{ + "smithy": "2.0", + "shapes": { + "com.example#Operation": { + "type": "operation", + "input": { + "target": "com.example#OperationRequest" + }, + "output": { + "target": "com.example#OperationResponse" + } + }, + "com.example#OperationRequest": { + "type": "structure" + }, + "com.example#OperationResponse": { + "type": "structure" + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/custom-suffix.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/custom-suffix.smithy new file mode 100644 index 00000000000..3e72cff0183 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/custom-suffix.smithy @@ -0,0 +1,10 @@ +$version: "2.0" +$operationInputSuffix: "Request" +$operationOutputSuffix: "Response" + +namespace com.example + +operation Operation { + input := {} + output := {} +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.json new file mode 100644 index 00000000000..199025aef5d --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.json @@ -0,0 +1,184 @@ +{ + "smithy": "2.0", + "shapes": { + "com.example#DerivedNames": { + "type": "operation", + "input": { + "target": "com.example#DerivedNamesInput" + }, + "output": { + "target": "com.example#DerivedNamesOutput" + } + }, + "com.example#DerivedNamesInput": { + "type": "structure", + "members": { + "id": { + "target": "smithy.api#String" + } + } + }, + "com.example#DerivedNamesOutput": { + "type": "structure", + "members": { + "id": { + "target": "smithy.api#String" + } + } + }, + "com.example#UsesTraits": { + "type": "operation", + "input": { + "target": "com.example#UsesTraitsInput" + }, + "output": { + "target": "com.example#UsesTraitsOutput" + } + }, + "com.example#UsesTraitsInput": { + "type": "structure", + "members": { + "id": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#sensitive": {} + } + }, + "com.example#UsesTraitsOutput": { + "type": "structure", + "members": { + "id": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#sensitive": {} + } + }, + "com.example#NameBearer": { + "type": "structure", + "members": { + "name": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "com.example#UsesMixins": { + "type": "operation", + "input": { + "target": "com.example#UsesMixinsInput" + }, + "output": { + "target": "com.example#UsesMixinsOutput" + } + }, + "com.example#UsesMixinsInput": { + "type": "structure", + "members": { + "id": { + "target": "smithy.api#String" + } + }, + "mixins": [ + { + "target": "com.example#NameBearer" + } + ] + }, + "com.example#UsesMixinsOutput": { + "type": "structure", + "members": { + "id": { + "target": "smithy.api#String" + } + }, + "mixins": [ + { + "target": "com.example#NameBearer" + } + ] + }, + "com.example#UsesTraitsAndMixins": { + "type": "operation", + "input": { + "target": "com.example#UsesTraitsAndMixinsInput" + }, + "output": { + "target": "com.example#UsesTraitsAndMixinsOutput" + } + }, + "com.example#UsesTraitsAndMixinsInput": { + "type": "structure", + "members": { + "id": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#sensitive": {} + }, + "mixins": [ + { + "target": "com.example#NameBearer" + } + ] + }, + "com.example#UsesTraitsAndMixinsOutput": { + "type": "structure", + "members": { + "id": { + "target": "smithy.api#String" + } + }, + "traits": { + "smithy.api#sensitive": {} + }, + "mixins": [ + { + "target": "com.example#NameBearer" + } + ] + }, + "com.example#EmptyShapes": { + "type": "operation", + "input": { + "target": "com.example#EmptyShapesInput" + }, + "output": { + "target": "com.example#EmptyShapesOutput" + } + }, + "com.example#EmptyShapesInput": { + "type": "structure" + }, + "com.example#EmptyShapesOutput": { + "type": "structure" + }, + "com.example#HasDocComments": { + "type": "operation", + "input": { + "target": "com.example#HasDocCommentsInput" + }, + "output": { + "target": "com.example#HasDocCommentsOutput" + } + }, + "com.example#HasDocCommentsInput": { + "type": "structure", + "traits": { + "smithy.api#documentation": "The trait parser automagically handles these" + } + }, + "com.example#HasDocCommentsOutput": { + "type": "structure", + "traits": { + "smithy.api#documentation": "Here too" + } + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.smithy new file mode 100644 index 00000000000..6824166c4d8 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.smithy @@ -0,0 +1,69 @@ +$version: "2.0" + +namespace com.example + +operation DerivedNames { + input := { + id: String + } + + output := { + id: String + } +} + +operation UsesTraits { + input := @sensitive { + id: String + } + + output := + @sensitive + { + id: String + } +} + +@mixin +structure NameBearer { + name: String +} + +operation UsesMixins { + input := with NameBearer { + id: String + } + + output := with NameBearer { + id: String + } +} + +operation UsesTraitsAndMixins { + input := + @sensitive + with NameBearer { + id: String + } + + output := + @sensitive + with NameBearer { + id: String + } +} + +operation EmptyShapes { + input := {} + output := {} +} + +operation HasDocComments { + input := + /// The trait parser automagically handles these + {} + + output := + /// Here too + {} +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/inline-io.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/inline-io.smithy new file mode 100644 index 00000000000..90cfbf7541d --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/inline-io.smithy @@ -0,0 +1,66 @@ +$version: "1.1" + +namespace com.example + +operation DerivedNames { + input := { + id: String + } + output := { + id: String + } +} + +operation EmptyShapes { + input := {} + output := {} +} + +operation HasDocComments { + input := + /// The trait parser automagically handles these + {} + output := + /// Here too + {} +} + +operation UsesMixins { + input := with NameBearer { + id: String + } + output := with NameBearer { + id: String + } +} + +operation UsesTraits { + input := + @sensitive + { + id: String + } + output := + @sensitive + { + id: String + } +} + +operation UsesTraitsAndMixins { + input := + @sensitive + with NameBearer { + id: String + } + output := + @sensitive + with NameBearer { + id: String + } +} + +@mixin +structure NameBearer { + name: String +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-shapes.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-shapes.smithy index f7c2ad549f8..15613b5f016 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-shapes.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-shapes.smithy @@ -49,8 +49,8 @@ resource SubResource { operation EmptyOperation {} operation MyOperation { - input: InputOutput - output: InputOutput + input := {} + output := {} errors: [ Error ] @@ -58,19 +58,17 @@ operation MyOperation { @readonly operation ReadonlyResourceOperation { - input: ResourceOperationInput + input := { + id: String + } } @idempotent operation ResourceOperation { - input: ResourceOperationInput + input := { + id: String + } } @error("client") structure Error {} - -structure InputOutput {} - -structure ResourceOperationInput { - id: String -}