Skip to content
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

Update IDL serializer to write metadata to a separate file #2464

Merged
merged 1 commit into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
import software.amazon.smithy.model.traits.Trait;
import software.amazon.smithy.model.traits.UnitTypeTrait;
import software.amazon.smithy.utils.AbstractCodeWriter;
import software.amazon.smithy.utils.CodeWriter;
import software.amazon.smithy.utils.FunctionalUtils;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.Pair;
import software.amazon.smithy.utils.SimpleCodeWriter;
import software.amazon.smithy.utils.SmithyBuilder;
import software.amazon.smithy.utils.StringUtils;

Expand Down Expand Up @@ -154,13 +154,22 @@ public Map<Path, String> serialize(Model model) {
.filter(shapeFilter)
.collect(Collectors.groupingBy(shapePlacer)).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> serialize(model, entry.getValue())));
// If there is no metadata, do not create metadata file
if (model.getMetadata().isEmpty()) {
return result;
}

// Add metadata file to the list of output files
Path metadataPath = Paths.get("metadata.smithy");
if (basePath != null) {
metadataPath = basePath.resolve(metadataPath);
}
if (result.isEmpty()) {
Path path = Paths.get("metadata.smithy");
if (basePath != null) {
path = basePath.resolve(path);
}
return Collections.singletonMap(path, serializeHeader(
model, null, Collections.emptySet(), inlineInputSuffix, inlineOutputSuffix));
return Collections.singletonMap(metadataPath, serializeHeader(
model, null, Collections.emptySet(), inlineInputSuffix, inlineOutputSuffix, true));
} else {
result.put(metadataPath, serializeHeader(
model, null, Collections.emptySet(), inlineInputSuffix, inlineOutputSuffix, true));
}
return result;
}
Expand Down Expand Up @@ -191,8 +200,8 @@ private String serialize(Model fullModel, Collection<Shape> shapes) {
.forEach(shape -> shape.accept(shapeSerializer));

String header = serializeHeader(
fullModel, namespace, shapes, inlineSuffixes.getLeft(), inlineSuffixes.getRight());
return header + codeWriter.toString();
fullModel, namespace, shapes, inlineSuffixes.getLeft(), inlineSuffixes.getRight(), false);
return header + codeWriter;
}

private Set<ShapeId> getInlineableShapes(
Expand Down Expand Up @@ -285,11 +294,10 @@ private String serializeHeader(
String namespace,
Collection<Shape> shapes,
String inputSuffix,
String outputSuffix
String outputSuffix,
boolean addMetadata
) {
SmithyCodeWriter codeWriter = new SmithyCodeWriter(null, fullModel);
NodeSerializer nodeSerializer = new NodeSerializer(codeWriter, fullModel);

codeWriter.write("$$version: \"$L\"", Model.MODEL_VERSION);

if (shapes.stream().anyMatch(Shape::isOperationShape)) {
Expand All @@ -304,23 +312,19 @@ private String serializeHeader(

codeWriter.write("");

Comparator<Map.Entry<String, Node>> comparator = componentOrder.metadataComparator();

// Write the full metadata into every output. When loaded back together the conflicts will be ignored,
// but if they're separated out then each file will still have all the context.
fullModel.getMetadata().entrySet().stream()
.filter(entry -> metadataFilter.test(entry.getKey()))
.sorted(comparator)
.forEach(entry -> {
codeWriter.trimTrailingSpaces(false)
.writeInline("metadata $K = ", entry.getKey())
.trimTrailingSpaces();
nodeSerializer.serialize(entry.getValue());
codeWriter.write("");
});

if (!fullModel.getMetadata().isEmpty()) {
codeWriter.write("");
if (addMetadata) {
NodeSerializer nodeSerializer = new NodeSerializer(codeWriter, fullModel);
Comparator<Map.Entry<String, Node>> comparator = componentOrder.metadataComparator();
fullModel.getMetadata().entrySet().stream()
.filter(entry -> metadataFilter.test(entry.getKey()))
.sorted(comparator)
.forEach(entry -> {
codeWriter.trimTrailingSpaces(false)
.writeInline("metadata $K = ", entry.getKey())
.trimTrailingSpaces();
nodeSerializer.serialize(entry.getValue());
codeWriter.write("");
});
}

if (namespace != null) {
Expand Down Expand Up @@ -606,7 +610,7 @@ private void writeMixins(Shape shape) {

private void writeShapeMembers(Collection<MemberShape> members) {
if (members.isEmpty()) {
// When the are no members to write, put "{}" on the same line.
// When there are no members to write, put "{}" on the same line.
codeWriter.writeInline("{}").write("");
} else {
codeWriter.openBlock("{", "}", () -> {
Expand Down Expand Up @@ -1028,7 +1032,7 @@ private void serializeKeyValuePairs(ObjectNode node, Shape shape) {
}

// If we're looking at a structure or union shape, we'll need to get the member shape based on the
// node key. Here we pre-compute a mapping so we don't have to traverse the member list every time.
// node key. Here we pre-compute a mapping, so we don't have to traverse the member list every time.
Map<String, MemberShape> members;
if (shape == null) {
members = Collections.emptyMap();
Expand Down Expand Up @@ -1058,11 +1062,11 @@ private void serializeKeyValuePairs(ObjectNode node, Shape shape) {
}

/**
* Extension of {@link CodeWriter} that provides additional convenience methods.
* Extension of {@link SimpleCodeWriter} that provides additional convenience methods.
*
* <p>Provides a built in $I formatter that formats shape ids, automatically trimming namespace where possible.
* <p>Provides a built-in $I formatter that formats shape ids, automatically trimming namespace where possible.
*/
private static final class SmithyCodeWriter extends CodeWriter {
private static final class SmithyCodeWriter extends SimpleCodeWriter {
private static final Pattern UNQUOTED_KEY_STRING = Pattern.compile("[a-zA-Z_][a-zA-Z_0-9]*");
private final String namespace;
private final Model model;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DynamicTest;
Expand All @@ -33,10 +36,11 @@
import software.amazon.smithy.utils.MapUtils;

public class SmithyIdlModelSerializerTest {
private static final URL TEST_FILE_URL = Objects.requireNonNull(SmithyIdlModelSerializer.class.getResource("idl-serialization/cases"));

@TestFactory
public Stream<DynamicTest> generateTests() throws IOException, URISyntaxException {
return Files.list(Paths.get(
SmithyIdlModelSerializer.class.getResource("idl-serialization/cases").toURI()))
return Files.list(Paths.get(TEST_FILE_URL.toURI()))
.map(path -> DynamicTest.dynamicTest(path.getFileName().toString(), () -> testConversion(path)));
}

Expand Down Expand Up @@ -79,10 +83,13 @@ public void filtersShapes() {
.build();
Map<Path, String> serialized = serializer.serialize(model);

assertThat(serialized, aMapWithSize(1));
assertThat(serialized, aMapWithSize(2));
assertThat(serialized, hasKey(Paths.get("ns.structures.smithy")));
assertThat(serialized.get(Paths.get("ns.structures.smithy")),
containsString("namespace ns.structures"));
assertThat(serialized, hasKey(Paths.get("metadata.smithy")));
assertThat(serialized.get(Paths.get("metadata.smithy")),
containsString("metadata shared = true"));
assertThat(serialized, not(hasKey(Paths.get("smithy.api.smithy"))));
}

Expand Down Expand Up @@ -236,15 +243,21 @@ public void usesOriginalSourceLocation() {

@Test
public void sortsAlphabetically() {
URL resource = getClass().getResource("idl-serialization/alphabetical.smithy");
Model model = Model.assembler().addImport(resource).assemble().unwrap();
URL before = getClass().getResource("idl-serialization/alphabetical/before.smithy");
URL after = getClass().getResource("idl-serialization/alphabetical/after.smithy");
URL metadata = getClass().getResource("idl-serialization/alphabetical/metadata.smithy");

Model model = Model.assembler().addImport(before).assemble().unwrap();
Map<Path, String> reserialized = SmithyIdlModelSerializer.builder()
.componentOrder(SmithyIdlComponentOrder.ALPHA_NUMERIC)
.build()
.serialize(model);
String modelResult = reserialized.values().iterator().next().replace("\r\n", "\n");

assertThat(modelResult, equalTo(IoUtils.readUtf8Url(resource).replace("\r\n", "\n")));
String modelResult = reserialized.get(Paths.get("com.example.smithy")).replace("\r\n", "\n");
String metadataResult = reserialized.get(Paths.get("metadata.smithy")).replace("\r\n", "\n");

assertThat(modelResult, equalTo(IoUtils.readUtf8Url(after).replace("\r\n", "\n")));
assertThat(metadataResult, equalTo(IoUtils.readUtf8Url(metadata).replace("\r\n", "\n")));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
$version: "2.0"

namespace com.example

structure Abc {
bar: String
@length(
min: 1
)
@required
baz: String
}

string Def

service Hij {
version: "2006-03-01"
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
$version: "2.0"

metadata foo = "hi"
metadata zoo = "test"
metadata foo = "hi"

namespace com.example

service Hij {
version: "2006-03-01"
}

string Def

structure Abc {
bar: String
@length(
Expand All @@ -13,9 +19,3 @@ structure Abc {
@required
baz: String
}

string Def

service Hij {
version: "2006-03-01"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
$version: "2.0"

metadata foo = "hi"
metadata zoo = "test"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
$version: "2.0"

metadata shared = true
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
$version: "2.0"

metadata shared = true

namespace ns.primitives

list StringList {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
$version: "2.0"

metadata shared = true

namespace ns.structures

use ns.primitives#StringList
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
$version: "2.0"

metadata zoo = "test"
metadata foo = "hi"

namespace com.example

string MyString
Expand Down
Loading