From 127dd780d2ed019bfb7a687d0c15ff1a8b616b83 Mon Sep 17 00:00:00 2001 From: Adwait Kumar Singh Date: Sat, 20 Dec 2025 14:45:31 -0800 Subject: [PATCH] Add utility to fill shapes with random values --- .../java/aws/events/AwsEventShapeEncoder.java | 10 +- .../pagination/PaginationInputSetter.java | 4 +- .../plugins/InjectIdempotencyTokenPlugin.java | 4 +- .../smithy/java/core/schema/SchemaUtils.java | 55 ----- .../smithy/java/core/schema/ShapeUtils.java | 219 ++++++++++++++++++ .../core/schema/SerializableStructTest.java | 39 ---- .../java/core/schema/ShapeUtilsTest.java | 63 +++++ .../CodecDeserializationFuzzTestBase.java | 3 +- .../java/fuzz/RandomShapeGenerator.java | 141 ----------- .../http/binding/HttpBindingSerializer.java | 4 +- 10 files changed, 295 insertions(+), 247 deletions(-) create mode 100644 core/src/main/java/software/amazon/smithy/java/core/schema/ShapeUtils.java delete mode 100644 core/src/test/java/software/amazon/smithy/java/core/schema/SerializableStructTest.java create mode 100644 core/src/test/java/software/amazon/smithy/java/core/schema/ShapeUtilsTest.java delete mode 100644 fuzz-test-harness/src/main/java/software/amazon/smithy/java/fuzz/RandomShapeGenerator.java diff --git a/aws/aws-event-streams/src/main/java/software/amazon/smithy/java/aws/events/AwsEventShapeEncoder.java b/aws/aws-event-streams/src/main/java/software/amazon/smithy/java/aws/events/AwsEventShapeEncoder.java index 4ccc7d10b..7f1a8e042 100644 --- a/aws/aws-event-streams/src/main/java/software/amazon/smithy/java/aws/events/AwsEventShapeEncoder.java +++ b/aws/aws-event-streams/src/main/java/software/amazon/smithy/java/aws/events/AwsEventShapeEncoder.java @@ -18,8 +18,8 @@ import software.amazon.eventstream.Message; import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; -import software.amazon.smithy.java.core.schema.SchemaUtils; import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeUtils; import software.amazon.smithy.java.core.schema.TraitKey; import software.amazon.smithy.java.core.serde.Codec; import software.amazon.smithy.java.core.serde.ShapeSerializer; @@ -75,7 +75,7 @@ private byte[] encodeInput( typeHolder.set(initialEventType.value()); var os = new ByteArrayOutputStream(); try (var baseSerializer = possibleTypes.get(initialEventType.value()).apply(os, headers)) { - SchemaUtils.withFilteredMembers(item.schema(), item, AwsEventShapeEncoder::excludeEventStreamMember) + ShapeUtils.withFilteredMembers(item.schema(), item, AwsEventShapeEncoder::excludeEventStreamMember) .serialize(baseSerializer); } return os.toByteArray(); @@ -202,14 +202,14 @@ public EventSerializer(EventHeaderSerializer headerSerializer, ShapeSerializer b @Override public void writeStruct(Schema schema, SerializableStruct struct) { if (hasEventPayloadMember(schema)) { - SchemaUtils.withFilteredMembers(schema, struct, this::isEventPayload) + ShapeUtils.withFilteredMembers(schema, struct, this::isEventPayload) .serializeMembers(baseSerializer); } else { - SchemaUtils.withFilteredMembers(schema, struct, this::isPayloadMember) + ShapeUtils.withFilteredMembers(schema, struct, this::isPayloadMember) .serialize(baseSerializer); } - SchemaUtils.withFilteredMembers(schema, struct, this::isHeadersMember) + ShapeUtils.withFilteredMembers(schema, struct, this::isHeadersMember) .serialize(headerSerializer); } diff --git a/client/client-core/src/main/java/software/amazon/smithy/java/client/core/pagination/PaginationInputSetter.java b/client/client-core/src/main/java/software/amazon/smithy/java/client/core/pagination/PaginationInputSetter.java index 7522565fd..23a092e58 100644 --- a/client/client-core/src/main/java/software/amazon/smithy/java/client/core/pagination/PaginationInputSetter.java +++ b/client/client-core/src/main/java/software/amazon/smithy/java/client/core/pagination/PaginationInputSetter.java @@ -7,8 +7,8 @@ import software.amazon.smithy.java.core.schema.ApiOperation; import software.amazon.smithy.java.core.schema.Schema; -import software.amazon.smithy.java.core.schema.SchemaUtils; import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeUtils; /** * Replaces values of a top-level structure members with values for pagination. @@ -35,7 +35,7 @@ final class PaginationInputSetter { I create(String token, Integer maxResults) { var builder = operation.inputBuilder(); - SchemaUtils.copyShape(input, builder); + ShapeUtils.copyShape(input, builder); if (token != null) { builder.setMemberValue(inputTokenSchema, token); } diff --git a/client/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/InjectIdempotencyTokenPlugin.java b/client/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/InjectIdempotencyTokenPlugin.java index 1752db734..4d3b27f2f 100644 --- a/client/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/InjectIdempotencyTokenPlugin.java +++ b/client/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/InjectIdempotencyTokenPlugin.java @@ -11,8 +11,8 @@ import software.amazon.smithy.java.client.core.interceptors.ClientInterceptor; import software.amazon.smithy.java.client.core.interceptors.InputHook; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.SchemaUtils; import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeUtils; import software.amazon.smithy.java.logging.InternalLogger; import software.amazon.smithy.utils.SmithyInternalApi; @@ -53,7 +53,7 @@ public I modifyBeforeSerialization(InputHook memberPredicate - ) { - return new SerializableStruct() { - @Override - public Schema schema() { - return schema; - } - - @Override - public void serializeMembers(ShapeSerializer serializer) { - struct.serializeMembers(new InterceptingSerializer() { - @Override - protected ShapeSerializer before(Schema schema) { - return memberPredicate.test(schema) ? serializer : ShapeSerializer.nullSerializer(); - } - }); - } - - @Override - public T getMemberValue(Schema member) { - return memberPredicate.test(schema()) ? struct.getMemberValue(member) : null; - } - }; - } - /** * Ensures that {@code member} is contained in {@code parent}, and if so returns {@code value}. * @@ -91,19 +51,4 @@ public static T validateSameMember(Schema expected, Schema actual, T value) + ": " + actual.id()); } - /** - * Attempts to copy the values from a struct into a shape builder. - * - * @param source The shape to copy from. - * @param sink The builder to copy into. - * @throws IllegalArgumentException if the two shapes are incompatible and don't use the same schemas. - */ - public static void copyShape(SerializableStruct source, ShapeBuilder sink) { - for (var member : source.schema().members()) { - var value = source.getMemberValue(member); - if (value != null) { - sink.setMemberValue(member, value); - } - } - } } diff --git a/core/src/main/java/software/amazon/smithy/java/core/schema/ShapeUtils.java b/core/src/main/java/software/amazon/smithy/java/core/schema/ShapeUtils.java new file mode 100644 index 000000000..299774744 --- /dev/null +++ b/core/src/main/java/software/amazon/smithy/java/core/schema/ShapeUtils.java @@ -0,0 +1,219 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.java.core.schema; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.time.Instant; +import java.util.HexFormat; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Predicate; +import java.util.stream.IntStream; +import software.amazon.smithy.java.core.serde.InterceptingSerializer; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.document.Document; +import software.amazon.smithy.java.io.ByteBufferUtils; +import software.amazon.smithy.model.shapes.ShapeType; + +public class ShapeUtils { + + private ShapeUtils() { + + } + + /** + * Create a serializable struct that only serializes members that pass the given predicate. + * + * @param schema Structure schema. + * @param struct Struct to serialize. + * @param memberPredicate Predicate that takes a member schema. + * @return the filtered struct. + */ + public static SerializableStruct withFilteredMembers( + Schema schema, + SerializableStruct struct, + Predicate memberPredicate + ) { + return new SerializableStruct() { + @Override + public Schema schema() { + return schema; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + struct.serializeMembers(new InterceptingSerializer() { + @Override + protected ShapeSerializer before(Schema schema) { + return memberPredicate.test(schema) ? serializer : ShapeSerializer.nullSerializer(); + } + }); + } + + @Override + public T getMemberValue(Schema member) { + return memberPredicate.test(schema()) ? struct.getMemberValue(member) : null; + } + }; + } + + /** + * Attempts to copy the values from a struct into a shape builder. + * + * @param source The shape to copy from. + * @param sink The builder to copy into. + * @throws IllegalArgumentException if the two shapes are incompatible and don't use the same schemas. + */ + public static void copyShape(SerializableStruct source, ShapeBuilder sink) { + for (var member : source.schema().members()) { + var value = source.getMemberValue(member); + if (value != null) { + sink.setMemberValue(member, value); + } + } + } + + /** + * Generates a random instance of a shape using the provided builder. + * + *

This method populates the shape with randomly generated values for all its members. + * Required members are always populated, while optional members have a 50% chance of being set. + * For union types, exactly one member is randomly selected and populated. + * + *

This is useful for fuzz testing and generating test data. + * + * @param shapeBuilder The builder to use for constructing the shape. + * @param The type of shape to generate. + * @return A randomly generated instance of the shape. + */ + public static T generateRandom(ShapeBuilder shapeBuilder) { + return shapeBuilder.deserialize(new RandomShapeGenerator()).build(); + } + + private static final class RandomShapeGenerator implements ShapeDeserializer { + @Override + public boolean readBoolean(Schema schema) { + return random().nextBoolean(); + } + + @Override + public ByteBuffer readBlob(Schema schema) { + var bytes = new byte[randomInt(100)]; + random().nextBytes(bytes); + return ByteBuffer.wrap(bytes); + } + + @Override + public byte readByte(Schema schema) { + return (byte) random().nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE + 1); + } + + @Override + public short readShort(Schema schema) { + return (short) random().nextInt(Short.MIN_VALUE, Short.MAX_VALUE + 1); + } + + @Override + public int readInteger(Schema schema) { + return random().nextInt(); + } + + @Override + public long readLong(Schema schema) { + return random().nextLong(); + } + + @Override + public float readFloat(Schema schema) { + return random().nextFloat(); + } + + @Override + public double readDouble(Schema schema) { + return random().nextDouble(); + } + + @Override + public BigInteger readBigInteger(Schema schema) { + int times = randomInt(3); + var bigInt = BigInteger.valueOf(random().nextLong()); + for (int i = 0; i < times; i++) { + bigInt = bigInt.multiply(BigInteger.valueOf(random().nextLong(Long.MAX_VALUE - 100, Long.MAX_VALUE))); + } + return bigInt; + } + + @Override + public BigDecimal readBigDecimal(Schema schema) { + var bigDecimal = new BigDecimal(readBigInteger(schema)); + int times = randomInt(); + while (times-- > 0) { + bigDecimal = bigDecimal.divide(new BigDecimal(readBigInteger(schema)), RoundingMode.CEILING); + } + return bigDecimal; + } + + @Override + public String readString(Schema schema) { + return HexFormat.of().formatHex(ByteBufferUtils.getBytes(readBlob(schema))); + } + + @Override + public Document readDocument() { + //TODO add more shapes. + return Document.of(readString(null)); + } + + @Override + public Instant readTimestamp(Schema schema) { + return Instant.now(); + } + + @Override + public void readStruct(Schema schema, T state, StructMemberConsumer consumer) { + if (schema.type().isShapeType(ShapeType.UNION)) { + var member = schema.members().get(randomInt(schema.members().size())); + consumer.accept(state, member, this); + } else { + for (var member : schema.members()) { + if (member.hasTrait(TraitKey.REQUIRED_TRAIT) || random().nextBoolean()) { + consumer.accept(state, member, this); + } + } + } + } + + @Override + public void readList(Schema schema, T state, ListMemberConsumer consumer) { + IntStream.range(0, randomInt()).forEach(i -> consumer.accept(state, this)); + } + + @Override + public void readStringMap(Schema schema, T state, MapMemberConsumer consumer) { + IntStream.range(0, randomInt()).forEach(i -> consumer.accept(state, readString(schema), this)); + } + + @Override + public boolean isNull() { + return random().nextBoolean(); + } + + private static ThreadLocalRandom random() { + return ThreadLocalRandom.current(); + } + + private static int randomInt() { + return random().nextInt(10); + } + + private static int randomInt(int bound) { + return random().nextInt(bound); + } + } +} diff --git a/core/src/test/java/software/amazon/smithy/java/core/schema/SerializableStructTest.java b/core/src/test/java/software/amazon/smithy/java/core/schema/SerializableStructTest.java deleted file mode 100644 index 98b9b6971..000000000 --- a/core/src/test/java/software/amazon/smithy/java/core/schema/SerializableStructTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.java.core.schema; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import software.amazon.smithy.java.core.serde.SpecificShapeSerializer; -import software.amazon.smithy.java.core.testmodels.Bird; - -public class SerializableStructTest { - @Test - public void filtersMembers() { - var struct = Bird.builder().name("foo").build(); - var filtered = SchemaUtils.withFilteredMembers(Bird.SCHEMA, struct, member -> false); - - var serializer = new SpecificShapeSerializer() { - private boolean wroteStruct; - - @Override - public void writeStruct(Schema schema, SerializableStruct struct) { - wroteStruct = true; - struct.serializeMembers(this); - } - }; - - // The filtered serializer doesn't serialize anything, so there's no exception. - filtered.serialize(serializer); - assertThat(serializer.wroteStruct, is(true)); - - // If anything is serialized to the serializer, it'll throw. - Assertions.assertThrows(RuntimeException.class, () -> struct.serialize(serializer)); - } -} diff --git a/core/src/test/java/software/amazon/smithy/java/core/schema/ShapeUtilsTest.java b/core/src/test/java/software/amazon/smithy/java/core/schema/ShapeUtilsTest.java new file mode 100644 index 000000000..a46e7fab4 --- /dev/null +++ b/core/src/test/java/software/amazon/smithy/java/core/schema/ShapeUtilsTest.java @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.java.core.schema; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; +import software.amazon.smithy.java.core.serde.SpecificShapeSerializer; +import software.amazon.smithy.java.core.testmodels.Bird; +import software.amazon.smithy.java.core.testmodels.Person; + +class ShapeUtilsTest { + @Test + void filtersMembers() { + var struct = Bird.builder().name("foo").build(); + var filtered = ShapeUtils.withFilteredMembers(Bird.SCHEMA, struct, member -> false); + + var serializer = new SpecificShapeSerializer() { + private boolean wroteStruct; + + @Override + public void writeStruct(Schema schema, SerializableStruct struct) { + wroteStruct = true; + struct.serializeMembers(this); + } + }; + + // The filtered serializer doesn't serialize anything, so there's no exception. + filtered.serialize(serializer); + assertThat(serializer.wroteStruct).isTrue(); + + // If anything is serialized to the serializer, it'll throw. + assertThatThrownBy(() -> struct.serialize(serializer)) + .isInstanceOf(RuntimeException.class); + } + + @RepeatedTest(10) + void generateRandomCreatesValidBird() { + Bird bird = ShapeUtils.generateRandom(Bird.builder()); + assertThat(bird).isNotNull(); + } + + @RepeatedTest(10) + void generateRandomCreatesValidPerson() { + Person person = ShapeUtils.generateRandom(Person.builder()); + assertThat(person).isNotNull(); + // name is required, should always be populated + assertThat(person.name()).isNotNull(); + } + + @RepeatedTest(10) + void generateRandomProducesDifferentResults() { + // Person has required name field, so it's always populated + Person person1 = ShapeUtils.generateRandom(Person.builder()); + Person person2 = ShapeUtils.generateRandom(Person.builder()); + assertThat(person1).isNotEqualTo(person2); + } +} diff --git a/fuzz-test-harness/src/main/java/software/amazon/smithy/java/fuzz/CodecDeserializationFuzzTestBase.java b/fuzz-test-harness/src/main/java/software/amazon/smithy/java/fuzz/CodecDeserializationFuzzTestBase.java index e8edd844b..39850d4be 100644 --- a/fuzz-test-harness/src/main/java/software/amazon/smithy/java/fuzz/CodecDeserializationFuzzTestBase.java +++ b/fuzz-test-harness/src/main/java/software/amazon/smithy/java/fuzz/CodecDeserializationFuzzTestBase.java @@ -19,6 +19,7 @@ import org.junit.jupiter.params.provider.MethodSource; import software.amazon.smithy.java.core.schema.SerializableShape; import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.schema.ShapeUtils; import software.amazon.smithy.java.core.serde.Codec; import software.amazon.smithy.java.core.serde.SerializationException; import software.amazon.smithy.java.io.ByteBufferUtils; @@ -60,7 +61,7 @@ private Stream seed() { return shapeBuilders.stream() .flatMap(b -> Stream.generate(() -> b).limit(10)) .map(Supplier::get) - .map(s -> s.deserialize(new RandomShapeGenerator()).build()) + .map(ShapeUtils::generateRandom) .map(t -> { try (var codec = this.codecToFuzz()) { return codec.serialize(t); diff --git a/fuzz-test-harness/src/main/java/software/amazon/smithy/java/fuzz/RandomShapeGenerator.java b/fuzz-test-harness/src/main/java/software/amazon/smithy/java/fuzz/RandomShapeGenerator.java deleted file mode 100644 index 388d21853..000000000 --- a/fuzz-test-harness/src/main/java/software/amazon/smithy/java/fuzz/RandomShapeGenerator.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.java.fuzz; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.nio.ByteBuffer; -import java.time.Instant; -import java.util.HexFormat; -import java.util.concurrent.ThreadLocalRandom; -import java.util.stream.IntStream; -import software.amazon.smithy.java.core.schema.Schema; -import software.amazon.smithy.java.core.schema.TraitKey; -import software.amazon.smithy.java.core.serde.ShapeDeserializer; -import software.amazon.smithy.java.core.serde.document.Document; -import software.amazon.smithy.java.io.ByteBufferUtils; -import software.amazon.smithy.model.shapes.ShapeType; - -final class RandomShapeGenerator implements ShapeDeserializer { - @Override - public boolean readBoolean(Schema schema) { - return random().nextBoolean(); - } - - @Override - public ByteBuffer readBlob(Schema schema) { - var bytes = new byte[randomInt(100)]; - random().nextBytes(bytes); - return ByteBuffer.wrap(bytes); - } - - @Override - public byte readByte(Schema schema) { - return (byte) random().nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE + 1); - } - - @Override - public short readShort(Schema schema) { - return (short) random().nextInt(Short.MIN_VALUE, Short.MAX_VALUE + 1); - } - - @Override - public int readInteger(Schema schema) { - return random().nextInt(); - } - - @Override - public long readLong(Schema schema) { - return random().nextLong(); - } - - @Override - public float readFloat(Schema schema) { - return random().nextFloat(); - } - - @Override - public double readDouble(Schema schema) { - return random().nextDouble(); - } - - @Override - public BigInteger readBigInteger(Schema schema) { - int times = randomInt(3); - var bigInt = BigInteger.valueOf(random().nextLong()); - for (int i = 0; i < times; i++) { - bigInt = bigInt.multiply(BigInteger.valueOf(random().nextLong(Long.MAX_VALUE - 100, Long.MAX_VALUE))); - } - return bigInt; - } - - @Override - public BigDecimal readBigDecimal(Schema schema) { - var bigDecimal = new BigDecimal(readBigInteger(schema)); - int times = randomInt(); - while (times-- > 0) { - bigDecimal = bigDecimal.divide(new BigDecimal(readBigInteger(schema)), RoundingMode.CEILING); - } - return bigDecimal; - } - - @Override - public String readString(Schema schema) { - return HexFormat.of().formatHex(ByteBufferUtils.getBytes(readBlob(schema))); - } - - @Override - public Document readDocument() { - return Document.of(readString(null)); - } - - @Override - public Instant readTimestamp(Schema schema) { - return Instant.now(); - } - - @Override - public void readStruct(Schema schema, T state, StructMemberConsumer consumer) { - if (schema.type().isShapeType(ShapeType.UNION)) { - var member = schema.members().get(randomInt(schema.members().size())); - consumer.accept(state, member, this); - } else { - for (var member : schema.members()) { - if (member.hasTrait(TraitKey.REQUIRED_TRAIT) || random().nextBoolean()) { - consumer.accept(state, member, this); - } - } - } - } - - @Override - public void readList(Schema schema, T state, ListMemberConsumer consumer) { - IntStream.range(0, randomInt()).forEach(i -> consumer.accept(state, this)); - } - - @Override - public void readStringMap(Schema schema, T state, MapMemberConsumer consumer) { - IntStream.range(0, randomInt()).forEach(i -> consumer.accept(state, readString(schema), this)); - } - - @Override - public boolean isNull() { - return random().nextBoolean(); - } - - private static ThreadLocalRandom random() { - return ThreadLocalRandom.current(); - } - - private static int randomInt() { - return random().nextInt(10); - } - - private static int randomInt(int bound) { - return random().nextInt(bound); - } -} diff --git a/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java b/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java index 58e5bfff3..20ec67365 100644 --- a/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java +++ b/http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java @@ -18,8 +18,8 @@ import java.util.function.BiConsumer; import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; -import software.amazon.smithy.java.core.schema.SchemaUtils; import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeUtils; import software.amazon.smithy.java.core.schema.TraitKey; import software.amazon.smithy.java.core.serde.Codec; import software.amazon.smithy.java.core.serde.InterceptingSerializer; @@ -124,7 +124,7 @@ public void writeStruct(Schema schema, SerializableStruct struct) { shapeBodyOutput = new ByteArrayOutputStream(); shapeBodySerializer = payloadCodec.createSerializer(shapeBodyOutput); // Serialize only the body members to the codec. - SchemaUtils.withFilteredMembers(schema, struct, this::bodyBindingPredicate) + ShapeUtils.withFilteredMembers(schema, struct, this::bodyBindingPredicate) .serialize(shapeBodySerializer); headers.put("content-type", List.of(payloadMediaType)); }