Skip to content

Commit 08e961d

Browse files
fix: event parsing iterator
The EventParsingIterator implementation does not parse correctly events when their payload do not have the eventpayload property set. The current behavior is that if said property "eventpayload" is not present then it fallback to extracting the value from the headers, but this is not a reliable method for parsing event streams since seems that not all services uses this pattern to provide the events. One example is the InvokeModelWithResponseStream operation from bedrock-runtime service. To fix this we pretty much followed the specs found in the smithy reference: https://smithy.io/2.0/spec/streaming.html#eventpayload-trait#id6, which explains that when eventpayload and eventheader is not set for any of the members of the shape then, the payload itself should be considered as the top level shape structure to be parsed. The test implementation for eventstream have also been refactored as follow: - We now have to specify the rest protocol that the input test data will be using, and based on that protocol we have a factory method to create either a rest xml or rest JSON parser. - Instead of validating if a specific member is present to validate if the parsed data type is the correct one, we now validate each parsed member to make sure they are all parsed into the expected data type.
1 parent 5b8a381 commit 08e961d

11 files changed

+374
-86
lines changed

src/Api/Parser/EventParsingIterator.php

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,37 +69,81 @@ private function parseEvent(array $event)
6969
if ($event['headers'][':message-type'] === 'error') {
7070
return $this->parseError($event);
7171
}
72+
7273
if ($event['headers'][':message-type'] !== 'event') {
7374
throw new ParserException('Failed to parse unknown message type.');
7475
}
7576
}
7677

77-
if (empty($event['headers'][':event-type'])) {
78+
$eventType = $event['headers'][':event-type'] ?? null;
79+
if (empty($eventType)) {
7880
throw new ParserException('Failed to parse without event type.');
7981
}
80-
$eventShape = $this->shape->getMember($event['headers'][':event-type']);
81-
82-
$parsedEvent = [];
83-
foreach ($eventShape['members'] as $shape => $details) {
84-
if (!empty($details['eventpayload'])) {
85-
$payloadShape = $eventShape->getMember($shape);
86-
if ($payloadShape['type'] === 'blob') {
87-
$parsedEvent[$shape] = $event['payload'];
82+
83+
$eventShape = $this->shape->getMember($eventType);
84+
$eventPayload = $event['payload'];
85+
86+
return [
87+
$eventType => array_merge(
88+
$this->parseEventHeaders($event['headers'], $eventShape),
89+
$this->parseEventPayload($eventPayload, $eventShape)
90+
)
91+
];
92+
}
93+
94+
/**
95+
* @param $headers
96+
* @param $eventShape
97+
*
98+
* @return array
99+
*/
100+
private function parseEventHeaders($headers, $eventShape): array
101+
{
102+
$parsedHeaders = [];
103+
foreach ($eventShape->getMembers() as $memberName => $memberProps) {
104+
if (isset($memberProps['eventheader'])) {
105+
$parsedHeaders[$memberName] = $headers[$memberName];
106+
}
107+
}
108+
109+
return $parsedHeaders;
110+
}
111+
112+
/**
113+
* @param $payload
114+
* @param $eventShape
115+
*
116+
* @return array
117+
*/
118+
private function parseEventPayload($payload, $eventShape): array
119+
{
120+
$parsedPayload = [];
121+
foreach ($eventShape->getMembers() as $memberName => $memberProps) {
122+
$memberShape = $eventShape->getMember($memberName);
123+
if (isset($memberProps['eventpayload'])) {
124+
if ($memberShape->getType() === 'blob') {
125+
$parsedPayload[$memberName] = $payload;
88126
} else {
89-
$parsedEvent[$shape] = $this->parser->parseMemberFromStream(
90-
$event['payload'],
91-
$payloadShape,
127+
$parsedPayload[$memberName] = $this->parser->parseMemberFromStream(
128+
$payload,
129+
$memberShape,
92130
null
93131
);
94132
}
95-
} else {
96-
$parsedEvent[$shape] = $event['headers'][$shape];
133+
134+
break;
97135
}
98136
}
99137

100-
return [
101-
$event['headers'][':event-type'] => $parsedEvent
102-
];
138+
if (empty($parsedPayload) && !empty($payload->getContents())) {
139+
/**
140+
* If we did not find a member with an eventpayload trait, then we should deserialize the payload
141+
* using the event's shape.
142+
*/
143+
$parsedPayload = $this->parser->parseMemberFromStream($payload, $eventShape, null);
144+
}
145+
146+
return $parsedPayload;
103147
}
104148

105149
private function parseError(array $event)
@@ -109,4 +153,4 @@ private function parseError(array $event)
109153
$event['headers'][':error-message']
110154
);
111155
}
112-
}
156+
}

0 commit comments

Comments
 (0)