|
5 | 5 |
|
6 | 6 | package software.amazon.smithy.java.aws.events; |
7 | 7 |
|
| 8 | +import java.util.Objects; |
| 9 | +import java.util.concurrent.Flow; |
8 | 10 | import java.util.function.Supplier; |
9 | 11 | import software.amazon.eventstream.Message; |
10 | 12 | import software.amazon.smithy.java.core.schema.Schema; |
11 | 13 | import software.amazon.smithy.java.core.schema.SerializableStruct; |
12 | 14 | import software.amazon.smithy.java.core.schema.ShapeBuilder; |
| 15 | +import software.amazon.smithy.java.core.schema.TraitKey; |
13 | 16 | import software.amazon.smithy.java.core.serde.Codec; |
| 17 | +import software.amazon.smithy.java.core.serde.ShapeDeserializer; |
| 18 | +import software.amazon.smithy.java.core.serde.SpecificShapeDeserializer; |
14 | 19 | import software.amazon.smithy.java.core.serde.event.EventDecoder; |
15 | 20 |
|
16 | | -public final class AwsEventShapeDecoder<E extends SerializableStruct> implements EventDecoder<AwsEventFrame> { |
| 21 | +/** |
| 22 | + * A decoder for AWS events |
| 23 | + * |
| 24 | + * @param <E> The type of the event |
| 25 | + * @param <IR> The type of the initial event |
| 26 | + */ |
| 27 | +public final class AwsEventShapeDecoder<E extends SerializableStruct, IR extends SerializableStruct> |
| 28 | + implements EventDecoder<AwsEventFrame> { |
17 | 29 |
|
| 30 | + private final InitialEventType initialEventType; |
| 31 | + private final Supplier<ShapeBuilder<IR>> initialEventBuilder; |
18 | 32 | private final Supplier<ShapeBuilder<E>> eventBuilder; |
19 | 33 | private final Schema eventSchema; |
20 | 34 | private final Codec codec; |
| 35 | + private volatile Flow.Publisher<SerializableStruct> publisher; |
21 | 36 |
|
22 | | - public AwsEventShapeDecoder( |
| 37 | + AwsEventShapeDecoder( |
| 38 | + InitialEventType initialEventType, |
| 39 | + Supplier<ShapeBuilder<IR>> initialEventBuilder, |
23 | 40 | Supplier<ShapeBuilder<E>> eventBuilder, |
24 | 41 | Schema eventSchema, |
25 | 42 | Codec codec |
26 | 43 | ) { |
27 | | - this.eventBuilder = eventBuilder; |
28 | | - this.eventSchema = eventSchema; |
29 | | - this.codec = codec; |
| 44 | + this.initialEventType = Objects.requireNonNull(initialEventType, "initialEventType"); |
| 45 | + this.initialEventBuilder = Objects.requireNonNull(initialEventBuilder, "initialEventBuilder"); |
| 46 | + this.eventBuilder = Objects.requireNonNull(eventBuilder, "eventBuilder"); |
| 47 | + this.eventSchema = Objects.requireNonNull(eventSchema, "eventSchema"); |
| 48 | + this.codec = Objects.requireNonNull(codec, "codec"); |
30 | 49 | } |
31 | 50 |
|
32 | 51 | @Override |
33 | | - public E decode(AwsEventFrame frame) { |
34 | | - Message message = frame.unwrap(); |
35 | | - String messageType = getMessageType(message); |
36 | | - if (!messageType.equals("event")) { |
37 | | - throw new UnsupportedOperationException("Unsupported frame type: " + messageType); |
| 52 | + public SerializableStruct decode(AwsEventFrame frame) { |
| 53 | + var message = frame.unwrap(); |
| 54 | + var eventType = getEventType(message); |
| 55 | + if (initialEventType.getName().equals(eventType)) { |
| 56 | + return decodeInitialResponse(frame); |
38 | 57 | } |
39 | | - String eventType = getEventType(message); |
40 | | - Schema memberSchema = eventSchema.member(eventType); |
| 58 | + return decodeEvent(frame); |
| 59 | + } |
| 60 | + |
| 61 | + @Override |
| 62 | + public void onPrepare(Flow.Publisher<SerializableStruct> publisher) { |
| 63 | + this.publisher = publisher; |
| 64 | + } |
| 65 | + |
| 66 | + private E decodeEvent(AwsEventFrame frame) { |
| 67 | + var message = frame.unwrap(); |
| 68 | + var eventType = getEventType(message); |
| 69 | + var memberSchema = eventSchema.member(eventType); |
41 | 70 | if (memberSchema == null) { |
42 | 71 | throw new IllegalArgumentException("Unsupported event type: " + eventType); |
43 | 72 | } |
| 73 | + var codecDeserializer = codec.createDeserializer(message.getPayload()); |
| 74 | + var eventDeserializer = new AwsEventDeserializer(memberSchema, codecDeserializer); |
| 75 | + var builder = eventBuilder.get(); |
| 76 | + return builder.deserialize(eventDeserializer).build(); |
| 77 | + } |
| 78 | + |
| 79 | + private IR decodeInitialResponse(AwsEventFrame frame) { |
| 80 | + var message = frame.unwrap(); |
| 81 | + var codecDeserializer = codec.createDeserializer(message.getPayload()); |
| 82 | + var builder = initialEventBuilder.get(); |
| 83 | + builder.deserialize(codecDeserializer); |
| 84 | + var publisherMember = getPublisherMember(builder.schema()); |
| 85 | + var responseDeserializer = new EventStreamDeserializer(publisherMember, publisher); |
| 86 | + builder.deserialize(responseDeserializer); |
| 87 | + return builder.build(); |
| 88 | + } |
44 | 89 |
|
45 | | - return eventBuilder.get() |
46 | | - .deserialize( |
47 | | - new AwsEventDeserializer( |
48 | | - memberSchema, |
49 | | - codec.createDeserializer(message.getPayload()))) |
50 | | - .build(); |
| 90 | + private Schema getPublisherMember(Schema schema) { |
| 91 | + for (var member : schema.members()) { |
| 92 | + if (member.memberTarget().hasTrait(TraitKey.STREAMING_TRAIT)) { |
| 93 | + return member; |
| 94 | + } |
| 95 | + } |
| 96 | + throw new IllegalArgumentException("cannot find streaming member"); |
51 | 97 | } |
52 | 98 |
|
53 | 99 | private String getEventType(Message message) { |
54 | 100 | return message.getHeaders().get(":event-type").getString(); |
55 | 101 | } |
56 | 102 |
|
57 | | - private String getMessageType(Message message) { |
58 | | - return message.getHeaders().get(":message-type").getString(); |
| 103 | + static class EventStreamDeserializer extends SpecificShapeDeserializer { |
| 104 | + private final Schema publisherMember; |
| 105 | + private final Flow.Publisher<? extends SerializableStruct> publisher; |
| 106 | + |
| 107 | + EventStreamDeserializer(Schema publisherMember, Flow.Publisher<? extends SerializableStruct> publisher) { |
| 108 | + this.publisherMember = publisherMember; |
| 109 | + this.publisher = publisher; |
| 110 | + } |
| 111 | + |
| 112 | + @Override |
| 113 | + public Flow.Publisher<? extends SerializableStruct> readEventStream(Schema schema) { |
| 114 | + return publisher; |
| 115 | + } |
| 116 | + |
| 117 | + @Override |
| 118 | + public <T> void readStruct(Schema schema, T state, ShapeDeserializer.StructMemberConsumer<T> consumer) { |
| 119 | + consumer.accept(state, publisherMember, this); |
| 120 | + } |
59 | 121 | } |
60 | 122 | } |
0 commit comments