diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java index 45a0f75ee90..83f9c71389d 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java @@ -207,50 +207,58 @@ public Node mapShape(MapShape shape) { @Override public Node operationShape(OperationShape shape) { + ShapeId id = shape.getId(); return withTraits(shape, createTypedNode(shape) - .withOptionalMember("input", shape.getInput().map(id -> Node.from(id.toString()))) - .withOptionalMember("output", shape.getOutput().map(id -> Node.from(id.toString()))) - .withOptionalMember("errors", createOptionalIdList(shape.getErrors()))); + .withOptionalMember("input", shape.getInput().map(target -> serializeShapeId(id, target))) + .withOptionalMember("output", shape.getOutput().map(target -> serializeShapeId(id, target))) + .withOptionalMember("errors", createOptionalIdList(id, shape.getErrors()))); } @Override public Node resourceShape(ResourceShape shape) { + ShapeId id = shape.getId(); + Optional identifiers = Optional.empty(); if (shape.hasIdentifiers()) { Stream> ids = shape.getIdentifiers().entrySet().stream(); identifiers = Optional.of(ids.collect(ObjectNode.collectStringKeys( Map.Entry::getKey, - entry -> Node.from(entry.getValue().toString())))); + entry -> serializeShapeId(id, entry.getValue())))); } return withTraits(shape, createTypedNode(shape) .withOptionalMember("identifiers", identifiers) - .withOptionalMember("put", shape.getPut().map(ShapeId::toString).map(Node::from)) - .withOptionalMember("create", shape.getCreate().map(ShapeId::toString).map(Node::from)) - .withOptionalMember("read", shape.getRead().map(ShapeId::toString).map(Node::from)) - .withOptionalMember("update", shape.getUpdate().map(ShapeId::toString).map(Node::from)) - .withOptionalMember("delete", shape.getDelete().map(ShapeId::toString).map(Node::from)) - .withOptionalMember("list", shape.getList().map(ShapeId::toString).map(Node::from)) - .withOptionalMember("operations", createOptionalIdList(shape.getOperations())) - .withOptionalMember("collectionOperations", createOptionalIdList(shape.getCollectionOperations())) - .withOptionalMember("resources", createOptionalIdList(shape.getResources()))); + .withOptionalMember("put", shape.getPut().map(target -> serializeShapeId(id, target))) + .withOptionalMember("create", shape.getCreate().map(target -> serializeShapeId(id, target))) + .withOptionalMember("read", shape.getRead().map(target -> serializeShapeId(id, target))) + .withOptionalMember("update", shape.getUpdate().map(target -> serializeShapeId(id, target))) + .withOptionalMember("delete", shape.getDelete().map(target -> serializeShapeId(id, target))) + .withOptionalMember("list", shape.getList().map(target -> serializeShapeId(id, target))) + .withOptionalMember("operations", createOptionalIdList(id, shape.getOperations())) + .withOptionalMember("collectionOperations", + createOptionalIdList(id, shape.getCollectionOperations())) + .withOptionalMember("resources", createOptionalIdList(id, shape.getResources()))); } @Override public Node serviceShape(ServiceShape shape) { + ShapeId id = shape.getId(); return withTraits(shape, createTypedNode(shape) .withMember("version", Node.from(shape.getVersion())) - .withOptionalMember("operations", createOptionalIdList(shape.getOperations())) - .withOptionalMember("resources", createOptionalIdList(shape.getResources()))); + .withOptionalMember("operations", createOptionalIdList(id, shape.getOperations())) + .withOptionalMember("resources", createOptionalIdList(id, shape.getResources()))); } - private Optional createOptionalIdList(Collection list) { + private Optional createOptionalIdList(ShapeId currentNamespace, Collection list) { if (list.isEmpty()) { return Optional.empty(); } - return Optional.of( - list.stream().map(ShapeId::toString).sorted().map(Node::from).collect(ArrayNode.collect())); + Node result = list.stream() + .map(id -> serializeShapeId(currentNamespace, id)) + .sorted(Comparator.comparing(StringNode::getValue)) + .collect(ArrayNode.collect()); + return Optional.of(result); } @Override @@ -275,11 +283,15 @@ private ObjectNode createStructureAndUnion(Shape shape, Map @Override public Node memberShape(MemberShape shape) { - String target = shape.getContainer().getNamespace().equals(shape.getTarget().getNamespace()) - ? shape.getTarget().asRelativeReference() - : shape.getTarget().toString(); + Node target = serializeShapeId(shape.getContainer(), shape.getTarget()); + return withTraits(shape, Node.objectNode().withMember("target", target)); + } - return withTraits(shape, Node.objectNode().withMember("target", Node.from(target))); + private StringNode serializeShapeId(ShapeId currentNamespace, ShapeId id) { + String target = currentNamespace.getNamespace().equals(id.getNamespace()) + ? id.asRelativeReference() + : id.toString(); + return Node.from(target); } } } diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ModelSerializerTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ModelSerializerTest.java index d266438e035..ec769f57e15 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ModelSerializerTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ModelSerializerTest.java @@ -149,4 +149,52 @@ public void doesNotSerializePreludeTraitsOrShapes() { assertFalse(serialized.getMember("smithy.api").isPresent()); } + + @Test + public void serializesAbsoluteShapeIdsOnlyWhenNeeded() { + Model model = Model.assembler() + .addImport(getClass().getResource("test-model.json")) + .assemble() + .unwrap(); + ModelSerializer serializer = ModelSerializer.builder().build(); + ObjectNode serialized = serializer.serialize(model); + + ObjectNode resourceWithRelativeIds = serialized.expectObjectMember("ns.foo") + .expectObjectMember("shapes") + .expectObjectMember("MyResource"); + ObjectNode resourceWithAbsoluteIds = serialized.expectObjectMember("ns.foo") + .expectObjectMember("shapes") + .expectObjectMember("ResourceNeedingAbsoluteShapeIds"); + ObjectNode structureWithAbsoluteIds = serialized.expectObjectMember("ns.resource.needing.ids") + .expectObjectMember("shapes") + .expectObjectMember("GetResourceNeedingAbsoluteShapeIdsInput"); + ObjectNode structureWithMixedIds = serialized.expectObjectMember("ns.foo") + .expectObjectMember("shapes") + .expectObjectMember("Structure"); + + assertThat(resourceWithAbsoluteIds.expectObjectMember("identifiers").expectStringMember("id").getValue(), + equalTo("ns.baz#String")); + assertThat(resourceWithAbsoluteIds.expectStringMember("read").getValue(), + equalTo("ns.resource.needing.ids#GetResourceNeedingAbsoluteShapeIds")); + + assertThat(resourceWithRelativeIds.expectObjectMember("identifiers").expectStringMember("id").getValue(), + equalTo("MyResourceId")); + assertThat(resourceWithRelativeIds.expectStringMember("put").getValue(), equalTo("PutMyResource")); + assertThat(resourceWithRelativeIds.expectStringMember("read").getValue(), equalTo("GetMyResource")); + assertThat(resourceWithRelativeIds.expectStringMember("delete").getValue(), equalTo("DeleteMyResource")); + assertThat(resourceWithRelativeIds.expectArrayMember("collectionOperations") + .get(0).get().expectStringNode().getValue(), + equalTo("BatchDeleteMyResource")); + + assertThat(structureWithAbsoluteIds.expectObjectMember("members").expectObjectMember("id") + .expectStringMember("target").getValue(), + equalTo("ns.baz#String")); + + assertThat(structureWithMixedIds.expectObjectMember("members").expectObjectMember("a") + .expectStringMember("target").getValue(), + equalTo("MyString")); + assertThat(structureWithMixedIds.expectObjectMember("members").expectObjectMember("c") + .expectStringMember("target").getValue(), + equalTo("ns.shapes#String")); + } } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/test-model.json b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/test-model.json index 8555c379f93..a6c7b061b40 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/test-model.json +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/test-model.json @@ -159,6 +159,9 @@ "a": { "target": "MyString", "required": true + }, + "c": { + "target": "ns.shapes#String" } }, "documentation": "abc" @@ -173,6 +176,41 @@ "a": { "type": "structure", "trait": true + }, + "OperationNeedingAbsoluteShapeIds": { + "type": "operation", + "idempotent": true, + "input": "ns.shapes#Structure", + "output": "ns.shapes#Structure", + "errors": ["ns.shapes#ErrorStructure"] + }, + "ResourceNeedingAbsoluteShapeIds": { + "type": "resource", + "identifiers": {"id": "ns.baz#String"}, + "read": "ns.resource.needing.ids#GetResourceNeedingAbsoluteShapeIds" + } + } + }, + "ns.resource.needing.ids": { + "shapes": { + "GetResourceNeedingAbsoluteShapeIds": { + "type": "operation", + "readonly": true, + "input": "GetResourceNeedingAbsoluteShapeIdsInput", + "output": "GetResourceNeedingAbsoluteShapeIdsOutput" + }, + "GetResourceNeedingAbsoluteShapeIdsInput": { + "type": "structure", + "members": { + "id": { + "target": "ns.baz#String", + "required": true + } + } + }, + "GetResourceNeedingAbsoluteShapeIdsOutput": { + "type": "structure", + "members": {} } } }, @@ -251,6 +289,10 @@ } } }, + "ErrorStructure": { + "type": "structure", + "error": "client" + }, "TaggedUnion": { "type": "union", "members": {