Resolve embed dependency before HAL rendering strips Request from body#174
Resolve embed dependency before HAL rendering strips Request from body#174koriym wants to merge 6 commits into
Conversation
Under HAL rendering, QueryRepository::put() called $ro->toString() before EtagSetter::setCacheDependency(), but HalRenderer::valuateElements() unsets Request entries from $ro->body and moves them under _embedded as decoded arrays. By the time setCacheDependency walked $ro->body looking for Request instances, none remained, so CacheDependency::depends() was never invoked and the parent's Surrogate-Key never gained the child URI tag. The miss was silent (no error, no log) and affected the documented #[Cacheable] + #[Embed] composition specifically under HAL — the JsonRenderer path was unaffected because it does not mutate $ro->body. Move the dependency walk into QueryRepository::put() and run it before $ro->toString(), while $ro->body still contains the original Request instances. EtagSetter and DevEtagSetter keep the CacheDependencyInterface constructor argument for backward compatibility but no longer perform the walk themselves. QueryRepository accepts an optional CacheDependencyInterface for DI; existing callers that omit it fall back to the default CacheDependency(new UriTag()). Test: testHalEmbeddedChildAddsChildSurrogateKeyToParent in CacheDependencyTest renders a #[Cacheable] parent that #[Embed]s a #[Cacheable] child under HalModule, then asserts the parent's stored Surrogate-Key contains the child URI tag.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughCache dependency resolution logic moves from EtagSetter and DevEtagSetter into QueryRepository. ETag setters retain backward-compatible constructor parameters but no longer store or use them; QueryRepository gains optional cache dependency injection and scans request bodies to register child resource dependencies during put() for HTTP 200 responses. A new HAL integration test validates the behavior with embedded resource fixtures. ChangesCache dependency resolution refactoring
🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 1.x #174 +/- ##
===========================================
Coverage 100.00% 100.00%
Complexity 246 246
===========================================
Files 53 53
Lines 746 750 +4
===========================================
+ Hits 746 750 +4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Constructors are implementation details resolved by Ray.Di, not part of the public contract. Remove the unused CacheDependencyInterface argument from EtagSetter/DevEtagSetter and make it a required constructor parameter on QueryRepository (no null fallback, no implicit CacheDependency(new UriTag()) default).
レビュアーガイド読む順序
核心 1 行
検証ポイント順序が正しいか
|
The body-walk used `instanceof Request` (the concrete final class in BEAR.Resource), which excluded sibling subclasses of `AbstractRequest` — notably `AsyncRequest` from bear/async. Async embed children were silently skipped from parent dependency tracking. `HalRenderer` itself uses `instanceof AbstractRequest` for the same walk. Match that abstraction level so AsyncRequest (and any future AbstractRequest decorator) is covered.
|
@coderabbitai Two follow-up commits pushed since the approval:
Please re-review. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainLet me fetch the latest changes from the PR to properly review both commits. 🏁 Script executed: Length of output: 13822 Let me check the DI module wiring before finalizing the review. 🏁 Script executed: Length of output: 10724
|
The body-walk drains BEAR\Async\PendingRequests on the first cast, so all queued async embeds resolve in parallel before the loop continues. Spell that out next to the (string) $body line so the somen-flow behaviour isn't lost on readers who only know the sync Request path.
setCacheDependency walks $ro->body and skips children that have no ETag header — i.e. embeds whose target resource isn't #[Cacheable]. That branch wasn't covered by the existing Dep fakes (all Cacheable), so a parent with a non-Cacheable embed could regress silently. New fakes + a single assertion exercise the continue: the parent's own Surrogate-Key is set, but the non-Cacheable child's URI tag must not appear in it.
「確認 SHOULD」フォローアップレビュアーガイドの 4 項目について調査結果を共有します。 ✅ Request::__toString() の 2 回呼び出しコスト実コストゼロです。 private function invoke(): ResourceObject
{
if ($this->result === null) {
$this->result = ($this)();
}
return $this->result;
}walk 内の AsyncRequest 側も同じ性質: ✅ 子が #[Cacheable] でない (ETag なし) ケースの continue パステストを追加しました (commit ❌ HAL レンダリングを使う既存テスト の回帰チェックレビュアーガイドのこの項目は前提が誤りでした (作成時の私の早とちり)。確認したところ ⏭ CDN purger 連携 (Fastly/Akamai)実環境を要するため CI 不可。本 PR のスコープ外として保留します (Surrogate-Key 自体の生成は単体テストで検証済み)。 追加コミット (このコメント以降の関連):
|
|
Tip For best results, initiate chat on the files or code changes.
Non-Cacheable child HAL regression check (self-corrected) — The correction is accurate. The scope boundary (existing tests = no HalModule, new test = only HAL path) is now clearly delineated, and the concern was indeed a false premise. CDN purger deferral — Correct call. Surrogate-Key header correctness is the contract being tested; downstream CDN behavior is an integration concern that belongs in an E2E or staging suite, not here. The PR is in excellent shape with all outstanding items resolved. 🐇 |
The earlier wording reached into BEAR\Async\PendingRequests by name, which crosses the abstraction boundary QueryRepository sits behind: it only contracts with AbstractRequest. Keep the comment on that abstraction — memoization plus shared invocation across casts — and let each request implementation own its own execution-strategy documentation.
Problem
#[Cacheable]parent +#[Embed]#[Cacheable]child under the HAL renderer never gets the child's URI tag merged into the parent'sSurrogate-Key. APUTon the child therefore does not invalidate the parent cache. The miss is silent — no error, no log line — and only affects the HAL representation, which is exactly the recommended representation for BEAR resources.The JSON renderer path is unaffected because
JsonRendererdoes not mutate$ro->body.Root cause
HalRenderer::valuateElements()(inbearsunday/BEAR.Resource) unsets everyAbstractRequestentry from$ro->bodyduring$ro->toString()and moves the rendered result under$ro->body['_embedded']:EtagSetter::setCacheDependency()ran aftertoString()and walked$ro->bodyforRequestinstances — by then every Request had been stripped, sodepends()was never called and the parent'sSurrogate-Keynever gained the child URI tag.Fix
Move the dependency walk into
QueryRepository::put()and run it before$ro->toString(), while the originalRequestinstances are still in$ro->body.The new walk:
SURROGATE_KEY(preservesCacheDependency::depends()'s assertion contract).Requestto string before reading its child ETag so lazy decorators (e.g.bear/async'sAsyncRequest) flush their batch.Constructor changes
EtagSetter/DevEtagSetter: drop unusedCacheDependencyInterfaceargument.QueryRepository: receiveCacheDependencyInterfaceas a required constructor argument.Test
tests/CacheDependencyTest::testHalEmbeddedChildAddsChildSurrogateKeyToParentrenders a#[Cacheable]parent that#[Embed]s a#[Cacheable]child underHalModule, then asserts the parent's storedSurrogate-Keycontains the child URI tag. Without the fix this assertion fails with an emptySurrogate-Key.Affected pattern surface
Any HAL+JSON resource using
#[Cacheable](or#[CacheableResponse]) on a parent with#[Embed]children. Reproduced and pinned in bearsunday/MyVendor.Cmsapp://self/cache/*showcase.