Use embed=body mode for stream feed iterators to support $all and system streams#77
Merged
Merged
Conversation
- Modify StreamFeedIterator to open feeds with EntryEmbedMode::BODY instead of fetching events individually via readEventBatch() - Add getEmbeddedEvent() to Entry class for accessing embedded event data - Update EntryDenormalizer to extract Event from embedded entry data - Update EventDenormalizer to handle null data (system events) and JSON string data (embed=body format) - Remove ClientExceptionInterface from iterator throws since readEventBatch is no longer called This resolves the issue where iterating over $all and system streams silently skipped events that couldn't be deserialized from individual event URLs. Using embed=body works for all stream types including $all and system streams with events that have no data body. Closes #71
Copilot
AI
changed the title
[WIP] Fix $all and system streams deserialization in StreamFeedIterator
Use embed=body mode for stream feed iterators to support $all and system streams
Jun 27, 2026
- EventDenormalizer: read both metadata and metaData keys so embedded
feed entries (which use capital-D) no longer silently drop metadata
- EventDenormalizer: map empty decoded metadata ([] from {}) back to null
to restore null-for-empty semantics required by integration tests
- EntryDenormalizer: guard embedded-event extraction behind BODY mode so
RICH feeds no longer fabricate Events with data=[]
- StreamFeedDenormalizer: forward $format and $context when calling
entryDenormalizer->denormalize so the embedMode context reaches
EntryDenormalizer
- StreamFeedIteratorTest: fix system-event test to account for forward
iterator's array_reverse ordering; use array_values + integer indexing
to avoid EntryWithEvent|false PHPStan error from reset(); remove
redundant assertContainsOnlyInstancesOf; use array_key_exists in
createStreamFeedWithEmbeddedEvents so explicit data:null is preserved
rather than coalesced to the default string
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011pprMw2pSRdHxxLBUpE4zb
EventDenormalizer::decodeField() now normalizes empty arrays to null, restoring the pre-existing contract that events without metadata expose null from getMetadata() (broken by the embed=body refactor). Fix the null-data system event test: feed entries are provided newest-first to match the forward iterator's array_reverse, and the mock helper honours an explicit null data value instead of substituting the default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PHPUnit 13 warns when a mock object has no configured expectations and suggests a stub instead. Convert value-provider collaborators from createMock() to createStub() across the unit tests, and where a shared mock property was used with expects() only in some tests, build a local mock in those tests and keep the shared property as a stub. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
StreamFeedIteratorusedreadEventBatch()to fetch events individually by URL. System events (e.g.$ProjectionCreated) return null content at their individual URLs, causing silent skips. The$allstream was similarly affected.The fix leverages
EntryEmbedMode::BODYwhen opening feeds, embedding event data directly in feed entries — eliminating the N+1 request pattern and working for all stream types.Changes
StreamFeedIterator— opens feeds withEntryEmbedMode::BODY, reads embedded events from entries instead of callingreadEventBatch()Entry— added?Event $embeddedEventconstructor param andgetEmbeddedEvent()accessorEntryDenormalizer— acceptsEventDenormalizer, extracts embedded event during entry denormalizationEventDenormalizer— relaxedsupportsDenormalization(no longer requiresdatakey), handles null data (system events) and JSON-string data (embed=body format) via newdecodeField()helperEventStoreFactory/SerializerFactory— adjusted instantiation order sinceEntryDenormalizernow depends onEventDenormalizerBefore/After