Shopware cache rework: route catalog reads through cacheable GET requests#19
Open
dm-heinze wants to merge 11 commits into
Open
Shopware cache rework: route catalog reads through cacheable GET requests#19dm-heinze wants to merge 11 commits into
dm-heinze wants to merge 11 commits into
Conversation
Upgrade the Shopware frontend stack to the versions that expose the cacheable GET read variants, foundation for the store-api catalog caching rework (epic #8). - @shopware/composables 1.11.1 -> 1.12.0 - @shopware/nuxt-module 1.4.4 -> 1.5.0 - @shopware/api-client 1.5.0 (pinned explicitly) - @shopware/helpers 1.7.1 (pinned explicitly; version the 1.5/1.12 stack resolves to) - @shopware/api-gen 1.5.0 (added as devDependency for type regen) composables, api-client and helpers are now explicit direct dependencies since the layer imports from them directly. cacheableReads stays unset (default off) - no read changes in this commit; behaviour is unchanged and backwards-compatible. npm lint passes (0 errors). api-gen type regeneration against a CACHE_REWORK backend is deferred - it needs live OPENAPI_JSON_URL / OPENAPI_ACCESS_KEY credentials not available in this environment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stand up automated unit testing for the repo, which previously had none.
- Add Vitest with @nuxt/test-utils/@vue/test-utils/happy-dom devDependencies
- Add `npm test` / `npm run test:watch` scripts; `pretest` runs `nuxt prepare`
so the generated .playground/.nuxt/tsconfig.json exists in a fresh checkout
- vitest.config.ts aliases Nuxt virtual modules (#imports, #app, #shopware,
#nuxtware) to lightweight test stubs so composables load without a full Nuxt
runtime
- test/setup.ts stubs Nuxt/Vue auto-import globals (useCookie, ref, ...)
- Demonstrate the core apiClient.invoke seam with a smoke test on useCurrency
that asserts the chosen operation string + HTTP method
("readCurrency post /currency")
- Document the seam and layout in docs/testing-guide.md
Gates: `npm test` and `npm lint` are green. `npx nuxt typecheck` is red on the
base commit already (pre-existing errors in app/ and server/); the new files
contribute zero type errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Store API types are now fetched per project (potentially from a
customer instance with custom endpoints/entities), so the generated
schema files must not be versioned. Only the overrides file is committed.
- Un-versioned generated schema: gitignore api-types/storeApiSchema.json
and api-types/storeApiTypes.d.ts; keep storeApiTypes.overrides.ts tracked
- Resolve all app/server typecheck errors without using `any`:
- Fix wrong property access, operation names and runtime-config paths
- Add undefined/null guards, type guards and narrow casts where values
are known (no `any`)
- Type provide/inject navigation as a ref (fixes runtime
`navigationItems.find is not a function` in mega-menu and sidenav)
- Build the change-profile body as a discriminated union instead of
mutating properties
- Base the contact/newsletter form slot types on CmsElementForm
- Base the CheckoutShipping price on currencyPrice
- Add @types/js-cookie and vue-tsc dev deps; install missing vitest
- Document type-declaration locations and the two known upstream
node_modules typecheck errors in CLAUDE.md (consolidated TypeScript section)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable `shopware: { cacheableReads: true }` in the layer nuxt.config so
anonymous catalog reads (SEO-URL, navigation, product-detail) route through
their cacheable GET variants — the Store-API Criteria is compressed into the
`?_criteria` query param, letting a CACHE_REWORK Shopware backend serve them
from its http_cache. SSR still renders the full requested result; only
cacheability changes, never the rendered DOM.
Add `test/helpers/cacheableRead.ts`, a reusable verification helper the
downstream read tickets (#4-#7) reuse: it parses the mocked
`apiClient.invoke` seam and asserts a given read issued a GET (correct
operation + method) carrying its Criteria in `query._criteria`, then returns
the response so the caller can assert the app renders from it.
`test/cacheableReads.test.ts` self-tests the helper and drives the real
flag-covered `useProductSearch` read to prove product-detail issues
`readProductDetailGet` when the flag is on and falls back to POST when off.
Out of scope (per ticket): asserting `s-maxage`/`Cache-Control` headers and
assigning the Shopware `store_api` caching policy (Shopware-side/ops); the
live "routes respond 200" check is an out-of-CI integration concern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire the redundant custom Nitro cache layer for SEO-URL and navigation
and let those reads flow through the GET-capable framework composables.
- [...dynamic].vue resolves paths via useNavigationSearch().resolvePath(),
handling both the raw SeoUrl and the resolved {routeName, foreignKey}
shapes (technical URLs covered as a superset); 404 behaviour preserved.
- LayoutHeader switches from the custom useNavigationCached to the
framework useNavigation().
- Remove the dead Nitro cache layer: the seo-url/navigation POST routes,
their cached server utils, the shared api client, useNavigationCached,
apiCacheLifetime, and the clear-cache + seo-url warmup routes.
- Add unit tests asserting the invoke seam issues the correct operation
and GET method for SEO-URL and navigation reads, and that both
resolvePath return shapes drive useNavigationContext.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With cacheableReads on, useProductSearch().search issues the GET variant (readProductDetailGet) and folds its associations into ?_criteria, so the product-detail page renders identically whether the framework GETs (flag on) or POSTs (flag off) — the method only affects cacheability, never the DOM. - Extract the manufacturer/media/cover associations into a shared, auto-imported productDetailAssociations constant so the component and the test assert the same payload; typed via `satisfies Schemas['Association']` so it stays a concrete, assignable type in both the app and stubbed test contexts. - Add decodeCriteria() to the cacheable-read helper (reverses encodeForQuery) so tests can inspect what a GET carried in query._criteria. - Unit tests: product-detail issues readProductDetailGet with all associations in _criteria (flag on), falls back to readProductDetail POST (flag off), and sends byte-identical Criteria either way (the DOM-parity guarantee). - Add an out-of-CI integration script that hits a real CACHE_REWORK backend and asserts the anonymous product-detail GET returns a render-ready cmsPage, building its Criteria exactly as the composable does (defu(cmsAssociations,…)). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`nuxt typecheck` only covered `test/nuxt/**`, so our root-level `test/**` specs were invisible to the CLI gate, while the IDE checked them against the minimal Vitest runtime stubs in test/tsconfig.json — surfacing false type errors the gate never saw. - .playground/nuxt.config.ts: add `test/**` to the generated tsconfig include so `nuxt typecheck` checks the specs with the real Nuxt types. - test/tsconfig.json: extend the generated Nuxt tsconfig instead of aliasing #imports/#shopware/#app to stubs. The stubs stay as runtime-only aliases in vitest.config.ts — types and runtime are now cleanly separated. Verified: nuxt typecheck now flags real errors inside test files and otherwise stays at the known 2-error node_modules baseline; vitest stays 24/24. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e engine (#15) Override useCategorySearch and useCategoryListing so anonymous catalog reads issue the cacheable GET variant of their route for allow-listed listing queries and fall back to POST for everything else. SSR sends the full Criteria and renders the complete result either way — the method only affects cacheability, never the DOM. - Add isCacheableListingQuery + the central CACHEABLE_LISTING_PARAMS allow-list ('p', 'limit', 'order'); a single non-allow-listed key forces POST. - useCategorySearch: wrap-and-spread override, only search replaced (rest via ...core). GET readCategoryGet with Criteria in ?_criteria for cacheable queries; POST readCategory otherwise. cmsAssociations rebuilt locally. - useCategoryListing: rebuilt around the framework's createListingComposable so only searchMethod changes; interactive pagination/limiter/sorter reads GET readProductListingGet for allow-listed queries, POST readProductListing otherwise. Shared state stays SDK-compatible (listingKey categoryListing). - Narrow _criteria cast bridges the typed GET operations that omit the compressed _criteria query param they accept at runtime. - Vitest matrices for both overrides (GET/POST choice, flag off, byte-identical criteria) plus out-of-CI integration scripts against a real backend. - Revise ADR decision 6: the interactive listing now rides the same engine. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gine (#16) Override useLandingSearch so anonymous landing reads issue the cacheable GET variant of the route for allow-listed listing queries and fall back to POST for everything else. SSR sends the full Criteria and renders the complete result (incl. cmsPage) either way — the method only affects cacheability, never the DOM. - useLandingSearch: wrap-and-spread override, only search replaced (rest via ...core). GET readLandingPageGet with Criteria in ?_criteria for cacheable queries; POST readLandingPage otherwise. Reuses #15's isCacheableListingQuery / CACHEABLE_LISTING_PARAMS allow-list (no duplicate predicate). - The typing gap is one step wider than category: readLandingPageGet is absent from the SDK operations union and operationPaths entirely (the route exists and accepts _criteria at runtime), so a locally typed invoke bridges just this one call. Self-contained and trivially removable once shopware-frontends ships the typed GET landing variant. - Associations mirror the framework landing composable exactly (single-level cmsAssociations.associations), so the GET/POST payload is byte-identical to the upstream read and cmsPage resolves unchanged. - Vitest matrix (GET/POST choice, flag off, byte-identical criteria) plus an out-of-CI integration script against a real backend. - Extend ADR consequences with the wider landing bridge; translate the ADR to English. Co-Authored-By: Claude Opus 4.8 (1M context) <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.
Summary
clear-cache.get,navigation.post,seo-url.post, andseo-url-cache-warmup.getroutes plus theshopware-get-cached-navigation/shopware-get-cached-seo-url/useNavigationCachedhelpers, in favour ofuseCategorySearch,useCategoryListing,useLandingSearchcomposables and shared_criteria/isCacheableListingQueryutils.storeApiTypes.overrides.ts.Test plan
npm run lintpassesnpx nuxt typecheckclean (only the 2 known upstream node_modules errors remain)npx vitest runpasses