From 3680e1b942aab2068ca19fb518e7376e68e3cf2e Mon Sep 17 00:00:00 2001 From: David Piggott Date: Tue, 13 Feb 2024 14:49:24 +0000 Subject: [PATCH 01/15] Define unknownFieldRetention trait --- ...re.amazon.smithy.model.traits.TraitService | 1 + .../core/resources/META-INF/smithy/manifest | 1 + .../smithy/unknownfieldretention.smithy | 9 +++ .../src/alloy/UnknownFieldRetentionTrait.java | 34 ++++++++++ .../core/src/alloy/UrlFormFlattenedTrait.java | 2 +- modules/core/src/alloy/UrlFormNameTrait.java | 2 +- .../resources/META-INF/smithy/traits.smithy | 13 ++++ ...knownFieldRetentionTraitProviderSpec.scala | 65 +++++++++++++++++++ .../UrlFormFlattenedTraitProviderSpec.scala | 2 +- .../alloy/UrlFormNameTraitProviderSpec.scala | 2 +- 10 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 modules/core/resources/META-INF/smithy/unknownfieldretention.smithy create mode 100644 modules/core/src/alloy/UnknownFieldRetentionTrait.java create mode 100644 modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala diff --git a/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index b92f40d..4de3feb 100644 --- a/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -23,5 +23,6 @@ alloy.StructurePatternTrait$Provider alloy.UrlFormFlattenedTrait$Provider alloy.UrlFormNameTrait$Provider alloy.UncheckedExamplesTrait$Provider +alloy.UnknownFieldRetentionTrait$Provider alloy.UntaggedUnionTrait$Provider alloy.UuidFormatTrait$Provider diff --git a/modules/core/resources/META-INF/smithy/manifest b/modules/core/resources/META-INF/smithy/manifest index 9613d4e..e9a7f3b 100644 --- a/modules/core/resources/META-INF/smithy/manifest +++ b/modules/core/resources/META-INF/smithy/manifest @@ -8,6 +8,7 @@ presence.smithy proto/proto.smithy restjson.smithy string.smithy +unknownfieldretention.smithy unions.smithy urlform.smithy uuid.smithy diff --git a/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy b/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy new file mode 100644 index 0000000..89da186 --- /dev/null +++ b/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy @@ -0,0 +1,9 @@ +$version: "2" + +namespace alloy + +/// Retain unknown fields of a containing structure in a map. +@trait( + selector: "structure > member :test(> map :test(> member > document))" +) +structure unknownFieldRetention {} diff --git a/modules/core/src/alloy/UnknownFieldRetentionTrait.java b/modules/core/src/alloy/UnknownFieldRetentionTrait.java new file mode 100644 index 0000000..f42f553 --- /dev/null +++ b/modules/core/src/alloy/UnknownFieldRetentionTrait.java @@ -0,0 +1,34 @@ +/* Copyright 2024 Disney Streaming + * + * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://disneystreaming.github.io/TOST-1.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 alloy; + +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AnnotationTrait; + +public final class UnknownFieldRetentionTrait extends AnnotationTrait { + public static ShapeId ID = ShapeId.from("alloy#unknownFieldRetention"); + + public UnknownFieldRetentionTrait() { + super(ID, Node.objectNode()); + } + + public static final class Provider extends AnnotationTrait.Provider { + public Provider() { + super(ID, (node) -> new UnknownFieldRetentionTrait()); + } + } +} diff --git a/modules/core/src/alloy/UrlFormFlattenedTrait.java b/modules/core/src/alloy/UrlFormFlattenedTrait.java index 488da84..91df3e5 100644 --- a/modules/core/src/alloy/UrlFormFlattenedTrait.java +++ b/modules/core/src/alloy/UrlFormFlattenedTrait.java @@ -1,4 +1,4 @@ -/* Copyright 2022 Disney Streaming +/* Copyright 2023 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/core/src/alloy/UrlFormNameTrait.java b/modules/core/src/alloy/UrlFormNameTrait.java index 83af818..ea05ab8 100644 --- a/modules/core/src/alloy/UrlFormNameTrait.java +++ b/modules/core/src/alloy/UrlFormNameTrait.java @@ -1,4 +1,4 @@ -/* Copyright 2022 Disney Streaming +/* Copyright 2023 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/core/test/resources/META-INF/smithy/traits.smithy b/modules/core/test/resources/META-INF/smithy/traits.smithy index 2c59b0a..0c65462 100644 --- a/modules/core/test/resources/META-INF/smithy/traits.smithy +++ b/modules/core/test/resources/META-INF/smithy/traits.smithy @@ -11,6 +11,7 @@ use alloy#openEnum use alloy#simpleRestJson use alloy#structurePattern use alloy#uncheckedExamples +use alloy#unknownFieldRetention use alloy#untagged use alloy#urlFormFlattened use alloy#urlFormName @@ -205,3 +206,15 @@ structure TestUrlFormName { @urlFormName("Test") test: String } + +map RetainedUnknownFields { + key: String + value: Document +} + +structure TestUnknownFieldRetention { + foo: String + bar: String + @unknownFieldRetention + bazes: RetainedUnknownFields +} diff --git a/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala b/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala new file mode 100644 index 0000000..27e187b --- /dev/null +++ b/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala @@ -0,0 +1,65 @@ +/* Copyright 2024 Disney Streaming + * + * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://disneystreaming.github.io/TOST-1.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 alloy + +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.DocumentShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.Model +import java.util.Optional + +final class UnknownFieldRetentionTraitProviderSpec extends munit.FunSuite { + + test("has trait") { + val documentShape = DocumentShape + .builder() + .id(ShapeId.fromParts("test", "MyDocument")) + .build() + val mapShape = ListShape + .builder() + .id(ShapeId.fromParts("test", "MyMap")) + .member(documentShape.getId) + .build() + val structId = ShapeId.fromParts("test", "MyStruct") + val targetId = structId.withMember("myMap") + val structShape = StructureShape + .builder() + .id(structId) + .addMember( + MemberShape + .builder() + .id(targetId) + .target(mapShape.getId) + .addTrait(new UnknownFieldRetentionTrait) + .build() + ) + .build() + + val model = + Model.assembler.disableValidation + .addShapes(structShape, mapShape, documentShape) + .assemble() + .unwrap() + + val result = model + .getShape(targetId) + .map(shape => shape.hasTrait(classOf[UnknownFieldRetentionTrait])) + + assertEquals(result, Optional.of(true)) + } +} diff --git a/modules/core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala b/modules/core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala index 8bb4b4b..c33f400 100644 --- a/modules/core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala @@ -1,4 +1,4 @@ -/* Copyright 2022 Disney Streaming +/* Copyright 2023 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala b/modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala index 0849249..1651d60 100644 --- a/modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala @@ -1,4 +1,4 @@ -/* Copyright 2022 Disney Streaming +/* Copyright 2023 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. From e536671327d5053bdc6bb375232abe1d61fe6b2d Mon Sep 17 00:00:00 2001 From: David Piggott Date: Tue, 13 Feb 2024 16:59:34 +0000 Subject: [PATCH 02/15] Workaround header check being too strict --- modules/core/src/alloy/UnknownFieldRetentionTrait.java | 2 +- modules/core/src/alloy/UrlFormFlattenedTrait.java | 2 +- modules/core/src/alloy/UrlFormNameTrait.java | 2 +- .../test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala | 2 +- .../core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala | 2 +- modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/core/src/alloy/UnknownFieldRetentionTrait.java b/modules/core/src/alloy/UnknownFieldRetentionTrait.java index f42f553..7f74335 100644 --- a/modules/core/src/alloy/UnknownFieldRetentionTrait.java +++ b/modules/core/src/alloy/UnknownFieldRetentionTrait.java @@ -1,4 +1,4 @@ -/* Copyright 2024 Disney Streaming +/* Copyright 2022 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/core/src/alloy/UrlFormFlattenedTrait.java b/modules/core/src/alloy/UrlFormFlattenedTrait.java index 91df3e5..488da84 100644 --- a/modules/core/src/alloy/UrlFormFlattenedTrait.java +++ b/modules/core/src/alloy/UrlFormFlattenedTrait.java @@ -1,4 +1,4 @@ -/* Copyright 2023 Disney Streaming +/* Copyright 2022 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/core/src/alloy/UrlFormNameTrait.java b/modules/core/src/alloy/UrlFormNameTrait.java index ea05ab8..83af818 100644 --- a/modules/core/src/alloy/UrlFormNameTrait.java +++ b/modules/core/src/alloy/UrlFormNameTrait.java @@ -1,4 +1,4 @@ -/* Copyright 2023 Disney Streaming +/* Copyright 2022 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala b/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala index 27e187b..fbd0ed7 100644 --- a/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala @@ -1,4 +1,4 @@ -/* Copyright 2024 Disney Streaming +/* Copyright 2022 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala b/modules/core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala index c33f400..8bb4b4b 100644 --- a/modules/core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/UrlFormFlattenedTraitProviderSpec.scala @@ -1,4 +1,4 @@ -/* Copyright 2023 Disney Streaming +/* Copyright 2022 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala b/modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala index 1651d60..0849249 100644 --- a/modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/UrlFormNameTraitProviderSpec.scala @@ -1,4 +1,4 @@ -/* Copyright 2023 Disney Streaming +/* Copyright 2022 Disney Streaming * * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. From cd7e6d5b2890560ec8f2e163d123a20815705bc8 Mon Sep 17 00:00:00 2001 From: David Piggott Date: Thu, 15 Feb 2024 17:23:43 +0000 Subject: [PATCH 03/15] Fix whitespace --- .../test/resources/META-INF/smithy/traits.smithy | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/core/test/resources/META-INF/smithy/traits.smithy b/modules/core/test/resources/META-INF/smithy/traits.smithy index 0c65462..07ea3f8 100644 --- a/modules/core/test/resources/META-INF/smithy/traits.smithy +++ b/modules/core/test/resources/META-INF/smithy/traits.smithy @@ -207,14 +207,14 @@ structure TestUrlFormName { test: String } +structure TestUnknownFieldRetention { + foo: String + bar: String + @unknownFieldRetention + bazes: RetainedUnknownFields +} + map RetainedUnknownFields { key: String - value: Document -} - -structure TestUnknownFieldRetention { - foo: String - bar: String - @unknownFieldRetention - bazes: RetainedUnknownFields + value: Document } From 706e8626f31222bfbaac5b77a12d636ffc477d12 Mon Sep 17 00:00:00 2001 From: David Piggott Date: Tue, 20 Feb 2024 17:20:54 +0000 Subject: [PATCH 04/15] Change target type to Document --- .../META-INF/smithy/unknownfieldretention.smithy | 2 +- .../core/test/resources/META-INF/smithy/traits.smithy | 7 +------ .../alloy/UnknownFieldRetentionTraitProviderSpec.scala | 10 ++-------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy b/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy index 89da186..0939658 100644 --- a/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy +++ b/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy @@ -4,6 +4,6 @@ namespace alloy /// Retain unknown fields of a containing structure in a map. @trait( - selector: "structure > member :test(> map :test(> member > document))" + selector: "structure > member :test(> document)" ) structure unknownFieldRetention {} diff --git a/modules/core/test/resources/META-INF/smithy/traits.smithy b/modules/core/test/resources/META-INF/smithy/traits.smithy index 07ea3f8..c4e17e8 100644 --- a/modules/core/test/resources/META-INF/smithy/traits.smithy +++ b/modules/core/test/resources/META-INF/smithy/traits.smithy @@ -211,10 +211,5 @@ structure TestUnknownFieldRetention { foo: String bar: String @unknownFieldRetention - bazes: RetainedUnknownFields -} - -map RetainedUnknownFields { - key: String - value: Document + bazes: Document } diff --git a/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala b/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala index fbd0ed7..f460b70 100644 --- a/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala @@ -15,7 +15,6 @@ package alloy -import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.DocumentShape @@ -30,11 +29,6 @@ final class UnknownFieldRetentionTraitProviderSpec extends munit.FunSuite { .builder() .id(ShapeId.fromParts("test", "MyDocument")) .build() - val mapShape = ListShape - .builder() - .id(ShapeId.fromParts("test", "MyMap")) - .member(documentShape.getId) - .build() val structId = ShapeId.fromParts("test", "MyStruct") val targetId = structId.withMember("myMap") val structShape = StructureShape @@ -44,7 +38,7 @@ final class UnknownFieldRetentionTraitProviderSpec extends munit.FunSuite { MemberShape .builder() .id(targetId) - .target(mapShape.getId) + .target(documentShape.getId) .addTrait(new UnknownFieldRetentionTrait) .build() ) @@ -52,7 +46,7 @@ final class UnknownFieldRetentionTraitProviderSpec extends munit.FunSuite { val model = Model.assembler.disableValidation - .addShapes(structShape, mapShape, documentShape) + .addShapes(structShape, documentShape) .assemble() .unwrap() From a741ba65dea704b4bdd2ae3f0d301f03ff1bf18c Mon Sep 17 00:00:00 2001 From: David Piggott Date: Wed, 6 Mar 2024 15:24:32 +0000 Subject: [PATCH 05/15] Define JSON variant --- ...re.amazon.smithy.model.traits.TraitService | 3 +- .../smithy/unknownfieldretention.smithy | 10 +++- .../UnknownDocumentFieldRetentionTrait.java | 34 +++++++++++ ...va => UnknownJsonFieldRetentionTrait.java} | 10 ++-- .../resources/META-INF/smithy/traits.smithy | 6 +- ...umentFieldRetentionTraitProviderSpec.scala | 60 +++++++++++++++++++ ...JsonFieldRetentionTraitProviderSpec.scala} | 6 +- 7 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 modules/core/src/alloy/UnknownDocumentFieldRetentionTrait.java rename modules/core/src/alloy/{UnknownFieldRetentionTrait.java => UnknownJsonFieldRetentionTrait.java} (75%) create mode 100644 modules/core/test/src/alloy/UnknownDocumentFieldRetentionTraitProviderSpec.scala rename modules/core/test/src/alloy/{UnknownFieldRetentionTraitProviderSpec.scala => UnknownJsonFieldRetentionTraitProviderSpec.scala} (88%) diff --git a/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index 4de3feb..2ee8b10 100644 --- a/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -23,6 +23,7 @@ alloy.StructurePatternTrait$Provider alloy.UrlFormFlattenedTrait$Provider alloy.UrlFormNameTrait$Provider alloy.UncheckedExamplesTrait$Provider -alloy.UnknownFieldRetentionTrait$Provider +alloy.UnknownDocumentFieldRetentionTrait$Provider +alloy.UnknownJsonFieldRetentionTrait$Provider alloy.UntaggedUnionTrait$Provider alloy.UuidFormatTrait$Provider diff --git a/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy b/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy index 0939658..c5ba1bf 100644 --- a/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy +++ b/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy @@ -3,7 +3,15 @@ $version: "2" namespace alloy /// Retain unknown fields of a containing structure in a map. +/// This variant controls the behaviour for document codecs. @trait( selector: "structure > member :test(> document)" ) -structure unknownFieldRetention {} +structure unknownDocumentFieldRetention {} + +/// Retain unknown fields of a containing structure in a map. +/// This variant controls the behaviour for JSON codecs. +@trait( + selector: "structure > member :test(> document)" +) +structure unknownJsonFieldRetention {} diff --git a/modules/core/src/alloy/UnknownDocumentFieldRetentionTrait.java b/modules/core/src/alloy/UnknownDocumentFieldRetentionTrait.java new file mode 100644 index 0000000..e6b169f --- /dev/null +++ b/modules/core/src/alloy/UnknownDocumentFieldRetentionTrait.java @@ -0,0 +1,34 @@ +/* Copyright 2022 Disney Streaming + * + * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://disneystreaming.github.io/TOST-1.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 alloy; + +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AnnotationTrait; + +public final class UnknownDocumentFieldRetentionTrait extends AnnotationTrait { + public static ShapeId ID = ShapeId.from("alloy#unknownDocumentFieldRetention"); + + public UnknownDocumentFieldRetentionTrait() { + super(ID, Node.objectNode()); + } + + public static final class Provider extends AnnotationTrait.Provider { + public Provider() { + super(ID, (node) -> new UnknownDocumentFieldRetentionTrait()); + } + } +} diff --git a/modules/core/src/alloy/UnknownFieldRetentionTrait.java b/modules/core/src/alloy/UnknownJsonFieldRetentionTrait.java similarity index 75% rename from modules/core/src/alloy/UnknownFieldRetentionTrait.java rename to modules/core/src/alloy/UnknownJsonFieldRetentionTrait.java index 7f74335..33243db 100644 --- a/modules/core/src/alloy/UnknownFieldRetentionTrait.java +++ b/modules/core/src/alloy/UnknownJsonFieldRetentionTrait.java @@ -19,16 +19,16 @@ import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.AnnotationTrait; -public final class UnknownFieldRetentionTrait extends AnnotationTrait { - public static ShapeId ID = ShapeId.from("alloy#unknownFieldRetention"); +public final class UnknownJsonFieldRetentionTrait extends AnnotationTrait { + public static ShapeId ID = ShapeId.from("alloy#unknownJsonFieldRetention"); - public UnknownFieldRetentionTrait() { + public UnknownJsonFieldRetentionTrait() { super(ID, Node.objectNode()); } - public static final class Provider extends AnnotationTrait.Provider { + public static final class Provider extends AnnotationTrait.Provider { public Provider() { - super(ID, (node) -> new UnknownFieldRetentionTrait()); + super(ID, (node) -> new UnknownJsonFieldRetentionTrait()); } } } diff --git a/modules/core/test/resources/META-INF/smithy/traits.smithy b/modules/core/test/resources/META-INF/smithy/traits.smithy index c4e17e8..36577d5 100644 --- a/modules/core/test/resources/META-INF/smithy/traits.smithy +++ b/modules/core/test/resources/META-INF/smithy/traits.smithy @@ -11,7 +11,8 @@ use alloy#openEnum use alloy#simpleRestJson use alloy#structurePattern use alloy#uncheckedExamples -use alloy#unknownFieldRetention +use alloy#unknownDocumentFieldRetention +use alloy#unknownJsonFieldRetention use alloy#untagged use alloy#urlFormFlattened use alloy#urlFormName @@ -210,6 +211,7 @@ structure TestUrlFormName { structure TestUnknownFieldRetention { foo: String bar: String - @unknownFieldRetention + @unknownDocumentFieldRetention + @unknownJsonFieldRetention bazes: Document } diff --git a/modules/core/test/src/alloy/UnknownDocumentFieldRetentionTraitProviderSpec.scala b/modules/core/test/src/alloy/UnknownDocumentFieldRetentionTraitProviderSpec.scala new file mode 100644 index 0000000..76092d9 --- /dev/null +++ b/modules/core/test/src/alloy/UnknownDocumentFieldRetentionTraitProviderSpec.scala @@ -0,0 +1,60 @@ +/* Copyright 2022 Disney Streaming + * + * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://disneystreaming.github.io/TOST-1.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 alloy + +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.DocumentShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.Model +import java.util.Optional + +final class UnknownDocumentFieldRetentionTraitProviderSpec + extends munit.FunSuite { + + test("has trait") { + val documentShape = DocumentShape + .builder() + .id(ShapeId.fromParts("test", "MyDocument")) + .build() + val structId = ShapeId.fromParts("test", "MyStruct") + val targetId = structId.withMember("myMap") + val structShape = StructureShape + .builder() + .id(structId) + .addMember( + MemberShape + .builder() + .id(targetId) + .target(documentShape.getId) + .addTrait(new UnknownDocumentFieldRetentionTrait) + .build() + ) + .build() + + val model = + Model.assembler.disableValidation + .addShapes(structShape, documentShape) + .assemble() + .unwrap() + + val result = model + .getShape(targetId) + .map(shape => shape.hasTrait(classOf[UnknownDocumentFieldRetentionTrait])) + + assertEquals(result, Optional.of(true)) + } +} diff --git a/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala b/modules/core/test/src/alloy/UnknownJsonFieldRetentionTraitProviderSpec.scala similarity index 88% rename from modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala rename to modules/core/test/src/alloy/UnknownJsonFieldRetentionTraitProviderSpec.scala index f460b70..eb6f303 100644 --- a/modules/core/test/src/alloy/UnknownFieldRetentionTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/UnknownJsonFieldRetentionTraitProviderSpec.scala @@ -22,7 +22,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.Model import java.util.Optional -final class UnknownFieldRetentionTraitProviderSpec extends munit.FunSuite { +final class UnknownJsonFieldRetentionTraitProviderSpec extends munit.FunSuite { test("has trait") { val documentShape = DocumentShape @@ -39,7 +39,7 @@ final class UnknownFieldRetentionTraitProviderSpec extends munit.FunSuite { .builder() .id(targetId) .target(documentShape.getId) - .addTrait(new UnknownFieldRetentionTrait) + .addTrait(new UnknownJsonFieldRetentionTrait) .build() ) .build() @@ -52,7 +52,7 @@ final class UnknownFieldRetentionTraitProviderSpec extends munit.FunSuite { val result = model .getShape(targetId) - .map(shape => shape.hasTrait(classOf[UnknownFieldRetentionTrait])) + .map(shape => shape.hasTrait(classOf[UnknownJsonFieldRetentionTrait])) assertEquals(result, Optional.of(true)) } From e270be09258b10cad0091e0c39c92d29f3bcf03b Mon Sep 17 00:00:00 2001 From: David Piggott Date: Thu, 7 Mar 2024 17:09:01 +0000 Subject: [PATCH 06/15] Add to simpleRestJson trait list --- modules/core/resources/META-INF/smithy/restjson.smithy | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/resources/META-INF/smithy/restjson.smithy b/modules/core/resources/META-INF/smithy/restjson.smithy index 8376eeb..7d7dfdb 100644 --- a/modules/core/resources/META-INF/smithy/restjson.smithy +++ b/modules/core/resources/META-INF/smithy/restjson.smithy @@ -26,6 +26,7 @@ namespace alloy smithy.api#range smithy.api#required smithy.api#timestampFormat + smithy.api#unknownJsonFieldRetention alloy#uuidFormat alloy#discriminated alloy#nullable From c6b6d1a28de2f89b1713ff6f8256a88c680a0b05 Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Tue, 6 Aug 2024 18:06:53 -0400 Subject: [PATCH 07/15] consolidate into a single jsonUnknown trait --- ...re.amazon.smithy.model.traits.TraitService | 3 +- .../META-INF/smithy/jsonunknown.smithy | 9 +++ .../core/resources/META-INF/smithy/manifest | 2 +- .../resources/META-INF/smithy/restjson.smithy | 2 +- .../smithy/unknownfieldretention.smithy | 17 ------ ...entionTrait.java => JsonUnknownTrait.java} | 10 ++-- .../UnknownDocumentFieldRetentionTrait.java | 34 ----------- .../resources/META-INF/smithy/traits.smithy | 8 +-- ...ala => JsonUnknownTraitProviderSpec.scala} | 6 +- ...umentFieldRetentionTraitProviderSpec.scala | 60 ------------------- 10 files changed, 23 insertions(+), 128 deletions(-) create mode 100644 modules/core/resources/META-INF/smithy/jsonunknown.smithy delete mode 100644 modules/core/resources/META-INF/smithy/unknownfieldretention.smithy rename modules/core/src/alloy/{UnknownJsonFieldRetentionTrait.java => JsonUnknownTrait.java} (75%) delete mode 100644 modules/core/src/alloy/UnknownDocumentFieldRetentionTrait.java rename modules/core/test/src/alloy/{UnknownJsonFieldRetentionTraitProviderSpec.scala => JsonUnknownTraitProviderSpec.scala} (88%) delete mode 100644 modules/core/test/src/alloy/UnknownDocumentFieldRetentionTraitProviderSpec.scala diff --git a/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index 2ee8b10..267889a 100644 --- a/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/modules/core/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -7,6 +7,7 @@ alloy.DataExamplesTrait$Provider alloy.DateFormatTrait$Provider alloy.DefaultValueTrait$Provider alloy.DiscriminatedUnionTrait$Provider +alloy.JsonUnknownTrait$Provider alloy.OpenEnumTrait$Provider alloy.NullableTrait$Provider alloy.openapi.OpenApiExtensionsTrait$Provider @@ -23,7 +24,5 @@ alloy.StructurePatternTrait$Provider alloy.UrlFormFlattenedTrait$Provider alloy.UrlFormNameTrait$Provider alloy.UncheckedExamplesTrait$Provider -alloy.UnknownDocumentFieldRetentionTrait$Provider -alloy.UnknownJsonFieldRetentionTrait$Provider alloy.UntaggedUnionTrait$Provider alloy.UuidFormatTrait$Provider diff --git a/modules/core/resources/META-INF/smithy/jsonunknown.smithy b/modules/core/resources/META-INF/smithy/jsonunknown.smithy new file mode 100644 index 0000000..ef867d1 --- /dev/null +++ b/modules/core/resources/META-INF/smithy/jsonunknown.smithy @@ -0,0 +1,9 @@ +$version: "2" + +namespace alloy + +/// Retain unknown fields of a containing structure in this document member +@trait( + selector: "structure > member :test(> document)" +) +structure jsonUnknown {} diff --git a/modules/core/resources/META-INF/smithy/manifest b/modules/core/resources/META-INF/smithy/manifest index e9a7f3b..f696ee0 100644 --- a/modules/core/resources/META-INF/smithy/manifest +++ b/modules/core/resources/META-INF/smithy/manifest @@ -8,7 +8,7 @@ presence.smithy proto/proto.smithy restjson.smithy string.smithy -unknownfieldretention.smithy +jsonunknown.smithy unions.smithy urlform.smithy uuid.smithy diff --git a/modules/core/resources/META-INF/smithy/restjson.smithy b/modules/core/resources/META-INF/smithy/restjson.smithy index 7d7dfdb..9611728 100644 --- a/modules/core/resources/META-INF/smithy/restjson.smithy +++ b/modules/core/resources/META-INF/smithy/restjson.smithy @@ -26,11 +26,11 @@ namespace alloy smithy.api#range smithy.api#required smithy.api#timestampFormat - smithy.api#unknownJsonFieldRetention alloy#uuidFormat alloy#discriminated alloy#nullable alloy#untagged + alloy#jsonUnknown ] ) @trait(selector: "service") diff --git a/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy b/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy deleted file mode 100644 index c5ba1bf..0000000 --- a/modules/core/resources/META-INF/smithy/unknownfieldretention.smithy +++ /dev/null @@ -1,17 +0,0 @@ -$version: "2" - -namespace alloy - -/// Retain unknown fields of a containing structure in a map. -/// This variant controls the behaviour for document codecs. -@trait( - selector: "structure > member :test(> document)" -) -structure unknownDocumentFieldRetention {} - -/// Retain unknown fields of a containing structure in a map. -/// This variant controls the behaviour for JSON codecs. -@trait( - selector: "structure > member :test(> document)" -) -structure unknownJsonFieldRetention {} diff --git a/modules/core/src/alloy/UnknownJsonFieldRetentionTrait.java b/modules/core/src/alloy/JsonUnknownTrait.java similarity index 75% rename from modules/core/src/alloy/UnknownJsonFieldRetentionTrait.java rename to modules/core/src/alloy/JsonUnknownTrait.java index 33243db..18457bc 100644 --- a/modules/core/src/alloy/UnknownJsonFieldRetentionTrait.java +++ b/modules/core/src/alloy/JsonUnknownTrait.java @@ -19,16 +19,16 @@ import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.AnnotationTrait; -public final class UnknownJsonFieldRetentionTrait extends AnnotationTrait { - public static ShapeId ID = ShapeId.from("alloy#unknownJsonFieldRetention"); +public final class JsonUnknownTrait extends AnnotationTrait { + public static ShapeId ID = ShapeId.from("alloy#jsonUnknown"); - public UnknownJsonFieldRetentionTrait() { + public JsonUnknownTrait() { super(ID, Node.objectNode()); } - public static final class Provider extends AnnotationTrait.Provider { + public static final class Provider extends AnnotationTrait.Provider { public Provider() { - super(ID, (node) -> new UnknownJsonFieldRetentionTrait()); + super(ID, (node) -> new JsonUnknownTrait()); } } } diff --git a/modules/core/src/alloy/UnknownDocumentFieldRetentionTrait.java b/modules/core/src/alloy/UnknownDocumentFieldRetentionTrait.java deleted file mode 100644 index e6b169f..0000000 --- a/modules/core/src/alloy/UnknownDocumentFieldRetentionTrait.java +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright 2022 Disney Streaming - * - * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://disneystreaming.github.io/TOST-1.0.txt - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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 alloy; - -import software.amazon.smithy.model.node.Node; -import software.amazon.smithy.model.shapes.ShapeId; -import software.amazon.smithy.model.traits.AnnotationTrait; - -public final class UnknownDocumentFieldRetentionTrait extends AnnotationTrait { - public static ShapeId ID = ShapeId.from("alloy#unknownDocumentFieldRetention"); - - public UnknownDocumentFieldRetentionTrait() { - super(ID, Node.objectNode()); - } - - public static final class Provider extends AnnotationTrait.Provider { - public Provider() { - super(ID, (node) -> new UnknownDocumentFieldRetentionTrait()); - } - } -} diff --git a/modules/core/test/resources/META-INF/smithy/traits.smithy b/modules/core/test/resources/META-INF/smithy/traits.smithy index 36577d5..e5d3aff 100644 --- a/modules/core/test/resources/META-INF/smithy/traits.smithy +++ b/modules/core/test/resources/META-INF/smithy/traits.smithy @@ -11,8 +11,7 @@ use alloy#openEnum use alloy#simpleRestJson use alloy#structurePattern use alloy#uncheckedExamples -use alloy#unknownDocumentFieldRetention -use alloy#unknownJsonFieldRetention +use alloy#jsonUnknown use alloy#untagged use alloy#urlFormFlattened use alloy#urlFormName @@ -208,10 +207,9 @@ structure TestUrlFormName { test: String } -structure TestUnknownFieldRetention { +structure TestJsonUnknown { foo: String bar: String - @unknownDocumentFieldRetention - @unknownJsonFieldRetention + @jsonUnknown bazes: Document } diff --git a/modules/core/test/src/alloy/UnknownJsonFieldRetentionTraitProviderSpec.scala b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala similarity index 88% rename from modules/core/test/src/alloy/UnknownJsonFieldRetentionTraitProviderSpec.scala rename to modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala index eb6f303..c32481d 100644 --- a/modules/core/test/src/alloy/UnknownJsonFieldRetentionTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala @@ -22,7 +22,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.Model import java.util.Optional -final class UnknownJsonFieldRetentionTraitProviderSpec extends munit.FunSuite { +final class JsonUnknownTraitProviderSpec extends munit.FunSuite { test("has trait") { val documentShape = DocumentShape @@ -39,7 +39,7 @@ final class UnknownJsonFieldRetentionTraitProviderSpec extends munit.FunSuite { .builder() .id(targetId) .target(documentShape.getId) - .addTrait(new UnknownJsonFieldRetentionTrait) + .addTrait(new JsonUnknownTrait) .build() ) .build() @@ -52,7 +52,7 @@ final class UnknownJsonFieldRetentionTraitProviderSpec extends munit.FunSuite { val result = model .getShape(targetId) - .map(shape => shape.hasTrait(classOf[UnknownJsonFieldRetentionTrait])) + .map(shape => shape.hasTrait(classOf[JsonUnknownTrait])) assertEquals(result, Optional.of(true)) } diff --git a/modules/core/test/src/alloy/UnknownDocumentFieldRetentionTraitProviderSpec.scala b/modules/core/test/src/alloy/UnknownDocumentFieldRetentionTraitProviderSpec.scala deleted file mode 100644 index 76092d9..0000000 --- a/modules/core/test/src/alloy/UnknownDocumentFieldRetentionTraitProviderSpec.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright 2022 Disney Streaming - * - * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://disneystreaming.github.io/TOST-1.0.txt - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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 alloy - -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.shapes.DocumentShape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.Model -import java.util.Optional - -final class UnknownDocumentFieldRetentionTraitProviderSpec - extends munit.FunSuite { - - test("has trait") { - val documentShape = DocumentShape - .builder() - .id(ShapeId.fromParts("test", "MyDocument")) - .build() - val structId = ShapeId.fromParts("test", "MyStruct") - val targetId = structId.withMember("myMap") - val structShape = StructureShape - .builder() - .id(structId) - .addMember( - MemberShape - .builder() - .id(targetId) - .target(documentShape.getId) - .addTrait(new UnknownDocumentFieldRetentionTrait) - .build() - ) - .build() - - val model = - Model.assembler.disableValidation - .addShapes(structShape, documentShape) - .assemble() - .unwrap() - - val result = model - .getShape(targetId) - .map(shape => shape.hasTrait(classOf[UnknownDocumentFieldRetentionTrait])) - - assertEquals(result, Optional.of(true)) - } -} From 94896396cba1b2446d98b0846dc57b00b69ecb8a Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Tue, 6 Aug 2024 18:12:41 -0400 Subject: [PATCH 08/15] enable model validation in provider spec --- modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala index c32481d..85f8b41 100644 --- a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala @@ -45,7 +45,7 @@ final class JsonUnknownTraitProviderSpec extends munit.FunSuite { .build() val model = - Model.assembler.disableValidation + Model.assembler .addShapes(structShape, documentShape) .assemble() .unwrap() From 653aa5c33b3e15b8355374c8a80969573477b9cc Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Fri, 9 Aug 2024 09:21:31 -0400 Subject: [PATCH 09/15] only allow jsonUnknown to be applied to a single structure member --- modules/core/resources/META-INF/smithy/jsonunknown.smithy | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/resources/META-INF/smithy/jsonunknown.smithy b/modules/core/resources/META-INF/smithy/jsonunknown.smithy index ef867d1..0c39943 100644 --- a/modules/core/resources/META-INF/smithy/jsonunknown.smithy +++ b/modules/core/resources/META-INF/smithy/jsonunknown.smithy @@ -5,5 +5,6 @@ namespace alloy /// Retain unknown fields of a containing structure in this document member @trait( selector: "structure > member :test(> document)" + structurallyExclusive: "member" ) structure jsonUnknown {} From cfac7155ebda68a409a4143ecf6130881177fdd4 Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Fri, 9 Aug 2024 09:45:57 -0400 Subject: [PATCH 10/15] test for exclusivity --- .../alloy/JsonUnknownTraitProviderSpec.scala | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala index 85f8b41..f41dbf4 100644 --- a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala @@ -15,13 +15,18 @@ package alloy +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.DocumentShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.shapes.DocumentShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.Model + import java.util.Optional +import scala.jdk.CollectionConverters._ +import scala.jdk.OptionConverters._ +import software.amazon.smithy.model.validation.Severity + final class JsonUnknownTraitProviderSpec extends munit.FunSuite { test("has trait") { @@ -56,4 +61,46 @@ final class JsonUnknownTraitProviderSpec extends munit.FunSuite { assertEquals(result, Optional.of(true)) } + + test("trait can only be applied to a single member") { + val source = + """|$version: "2" + | + |namespace test + | + |use alloy#jsonUnknown + | + |structure MyStruct { + | @jsonUnknown + | first: Document + | @jsonUnknown + | second: Document + |} + |""".stripMargin + + val result = + Model.assembler + .discoverModels() + .addUnparsedModel("/test.smithy", source) + .assemble() + + assert(result.isBroken()) + + val errors = result + .getValidationEvents() + .asScala + .filter(ev => ev.getSeverity() == Severity.ERROR) + .toList + + assertEquals(errors.length, 1) + + val List(theError) = errors + + assertEquals( + Some(ShapeId.from("test#MyStruct")), + theError.getShapeId().toScala + ) + assertEquals(theError.getId(), "ExclusiveStructureMemberTrait") + + } } From 8ba7695e658ca2dd9a9867291a0bc40af86ab24b Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Fri, 9 Aug 2024 09:49:44 -0400 Subject: [PATCH 11/15] refactor test to parse smithy source --- .../alloy/JsonUnknownTraitProviderSpec.scala | 48 ++++++++----------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala index f41dbf4..6ad0e95 100644 --- a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala @@ -16,47 +16,39 @@ package alloy import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.DocumentShape -import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.validation.Severity import java.util.Optional - -import scala.jdk.CollectionConverters._ -import scala.jdk.OptionConverters._ -import software.amazon.smithy.model.validation.Severity +import scala.jdk.CollectionConverters.* +import scala.jdk.OptionConverters.* final class JsonUnknownTraitProviderSpec extends munit.FunSuite { test("has trait") { - val documentShape = DocumentShape - .builder() - .id(ShapeId.fromParts("test", "MyDocument")) - .build() - val structId = ShapeId.fromParts("test", "MyStruct") - val targetId = structId.withMember("myMap") - val structShape = StructureShape - .builder() - .id(structId) - .addMember( - MemberShape - .builder() - .id(targetId) - .target(documentShape.getId) - .addTrait(new JsonUnknownTrait) - .build() - ) - .build() + val source = + """|$version: "2" + | + |namespace test + | + |use alloy#jsonUnknown + | + |document MyDocument + | + |structure MyStruct { + | @jsonUnknown + | myMap: MyDocument + |} + |""".stripMargin val model = - Model.assembler - .addShapes(structShape, documentShape) + Model.assembler.discoverModels() + .addUnparsedModel("/test.smithy", source) .assemble() .unwrap() val result = model - .getShape(targetId) + .getShape(ShapeId.from("test#MyStruct$myMap")) .map(shape => shape.hasTrait(classOf[JsonUnknownTrait])) assertEquals(result, Optional.of(true)) From 19bb229d3661f4faefb948d2590f8ec5bfd0123a Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Fri, 9 Aug 2024 14:09:01 -0400 Subject: [PATCH 12/15] modify jsonUnknown to only make it applicable to map with members of type Document --- .../resources/META-INF/smithy/jsonunknown.smithy | 2 +- .../test/resources/META-INF/smithy/traits.smithy | 7 ++++++- .../src/alloy/JsonUnknownTraitProviderSpec.scala | 16 ++++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/modules/core/resources/META-INF/smithy/jsonunknown.smithy b/modules/core/resources/META-INF/smithy/jsonunknown.smithy index 0c39943..02a09f4 100644 --- a/modules/core/resources/META-INF/smithy/jsonunknown.smithy +++ b/modules/core/resources/META-INF/smithy/jsonunknown.smithy @@ -4,7 +4,7 @@ namespace alloy /// Retain unknown fields of a containing structure in this document member @trait( - selector: "structure > member :test(> document)" + selector: "structure > member :test(> map > member > document)" structurallyExclusive: "member" ) structure jsonUnknown {} diff --git a/modules/core/test/resources/META-INF/smithy/traits.smithy b/modules/core/test/resources/META-INF/smithy/traits.smithy index 92e21c7..0cf7dbc 100644 --- a/modules/core/test/resources/META-INF/smithy/traits.smithy +++ b/modules/core/test/resources/META-INF/smithy/traits.smithy @@ -217,5 +217,10 @@ structure TestJsonUnknown { foo: String bar: String @jsonUnknown - bazes: Document + bazes: UnknownProps +} + +map UnknownProps { + key: String + value: Document } diff --git a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala index 6ad0e95..c018044 100644 --- a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala @@ -33,11 +33,14 @@ final class JsonUnknownTraitProviderSpec extends munit.FunSuite { | |use alloy#jsonUnknown | - |document MyDocument + |map UnknownProps { + | key: String + | value: Document + |} | |structure MyStruct { | @jsonUnknown - | myMap: MyDocument + | myMap: UnknownProps |} |""".stripMargin @@ -62,11 +65,16 @@ final class JsonUnknownTraitProviderSpec extends munit.FunSuite { | |use alloy#jsonUnknown | + |map UnknownProps { + | key: String + | value: Document + |} + | |structure MyStruct { | @jsonUnknown - | first: Document + | first: UnknownProps | @jsonUnknown - | second: Document + | second: UnknownProps |} |""".stripMargin From f85bb62237922f0e3e0bad757776a558f5d098f0 Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Mon, 12 Aug 2024 11:06:17 -0400 Subject: [PATCH 13/15] formatting --- modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala index c018044..a2abe4a 100644 --- a/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala +++ b/modules/core/test/src/alloy/JsonUnknownTraitProviderSpec.scala @@ -45,7 +45,8 @@ final class JsonUnknownTraitProviderSpec extends munit.FunSuite { |""".stripMargin val model = - Model.assembler.discoverModels() + Model.assembler + .discoverModels() .addUnparsedModel("/test.smithy", source) .assemble() .unwrap() From 8b858c5482231dd10d9aad985f9a1ed63108aeed Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Mon, 12 Aug 2024 15:44:41 -0400 Subject: [PATCH 14/15] update doc --- modules/core/resources/META-INF/smithy/jsonunknown.smithy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/resources/META-INF/smithy/jsonunknown.smithy b/modules/core/resources/META-INF/smithy/jsonunknown.smithy index 02a09f4..83f90d1 100644 --- a/modules/core/resources/META-INF/smithy/jsonunknown.smithy +++ b/modules/core/resources/META-INF/smithy/jsonunknown.smithy @@ -2,7 +2,7 @@ $version: "2" namespace alloy -/// Retain unknown fields of a containing structure in this document member +/// Retain unknown fields of a containing structure in this map member @trait( selector: "structure > member :test(> map > member > document)" structurallyExclusive: "member" From c3170e18d1ac0f9d91dcbbaf3fe8e64d64cbae43 Mon Sep 17 00:00:00 2001 From: Benoit Louy Date: Tue, 13 Aug 2024 10:32:06 -0400 Subject: [PATCH 15/15] add documentation --- docs/serialisation/json.md | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/serialisation/json.md b/docs/serialisation/json.md index f00fbdf..9854817 100644 --- a/docs/serialisation/json.md +++ b/docs/serialisation/json.md @@ -135,7 +135,7 @@ The JSON objects {} ``` -are respectively decoded as follows in Scala (when using [smithy4s](https://disneystreaming.github.io/smithy4s/)): +are respectively decoded as follows in Scala (when using [smithy4s](https://disneystreaming.github.io/smithy4s/)): ```scala Foo(Some(Nullable.Null), None) @@ -152,3 +152,46 @@ or some similar type which preserves the information that an explicit `null` was ``` This means that `@nullable` allows round-tripping null values. + + +#### Unknown fields + +Retaining JSON fields whose label do not match structure member names is supported via the `@jsonUnknown` smithy trait. This trait can be applied to a single structure member targeting a `map` with `Document` values. + +JSON decoders supporting this trait must store unknown properties in the annotated map. Symmetrically, JSON encoders must inline the values from the map in the JSON object produced when serializing the enclosing structure. + +Note that if a JSON document contains a field using the same label as the member annotated with the `@jsonUnknown` trait, it will be treated as an unknown field. + +For example, given the following smithy definitions + +```smithy +use alloy#jsonUnknown + +structure Data { + known: String + @jsonUnknown + unknown: UnknownProperties +} + +map UnknownProperties { + key: String + value: Document +} +``` + +The JSON objects + +```json +{ "known": "known value" } +{ "known": "known value", "aField": 1, "anotherField": "another value" } +{ "known": "known value", "unknown": 1 } +``` + +are respectively decoded as follows in Scala (when using [smithy4s](https://disneystreaming.github.io/smithy4s/)) + +```scala +Data(known=Some("known value"), unknown=None) +Data(known=Some("known value"), + unknown=Some(Map("aField" -> Document.DNunmber(1), "anotherField" -> Document.DString("another value")))) +Data(known=Some("known value"), unknown=Some(Map("unknown" -> Document.DNumber(1)))) +```