Skip to content

fix(query): fix mutationInvalidates missing args and wrong import path (#3090)#3197

Closed
zeriong wants to merge 0 commit intoorval-labs:masterfrom
zeriong:fix/mutation-invalidates-codegen-bugs-3090
Closed

fix(query): fix mutationInvalidates missing args and wrong import path (#3090)#3197
zeriong wants to merge 0 commit intoorval-labs:masterfrom
zeriong:fix/mutation-invalidates-codegen-bugs-3090

Conversation

@zeriong
Copy link
Copy Markdown
Contributor

@zeriong zeriong commented Apr 4, 2026

Summary

Closes #3090

Bug 1: Missing required arguments in generated invalidation code

When mutationInvalidates targets a query with required path parameters (e.g. showPetById) but no params mapping is specified, the generated code called the query key function without arguments:

// Before (broken): TS error + produces ['/pets/undefined']
queryClient.invalidateQueries({ queryKey: getShowPetByIdQueryKey() });

Now looks up the target query's route from the OpenAPI spec and generates predicate-based broad matching using the route prefix:

// After (fixed): matches all /pets/* queries via startsWith
queryClient.invalidateQueries({
  predicate: (query) => {
    const key = query.queryKey[0];
    return typeof key === 'string' && key.startsWith('/pets');
  },
});

Queries without required path params (e.g. listPets) and queries with explicit params mapping continue to use queryKey: getXxxQueryKey(...) as before.

Bug 2: Incorrect import path in tags-split mode

In tags-split mode, the file option for cross-tag invalidation generated import paths relative to the output root instead of the tag subdirectory:

// Before (broken): resolves to item/items (wrong)
import { getGetItemsQueryKey } from './items';

// After (fixed): resolves to ../items/items (correct)
import { getGetItemsQueryKey } from '../items/items';

Changes

File Change
packages/query/src/mutation-generator.ts Add findOperationRoute / getRoutePrefix helpers; generate predicate-based invalidation when params are missing for path-param queries
packages/core/src/writers/split-tags-mode.ts Adjust file importPath from ./tag to ../tag/tag in tags-split directory structure
tests/configs/react-query.config.ts Add showPetById without params to createPets invalidation for Bug 1 coverage
tests/__snapshots__/react-query/invalidates/endpoints.ts Updated snapshot

Test plan

  • Unit tests pass: bun vitest run (2178/2181, 3 pre-existing failures in resolve-version.test.ts)
  • Snapshot tests pass: bun run test:snapshots (67/67 tasks)
  • Verified generated snapshot contains predicate + startsWith('/pets') for params-less showPetById invalidation
  • Verified deletePetById with params: ['petId'] still generates queryKey: getShowPetByIdQueryKey(variables.petId)

Summary by CodeRabbit

  • New Features

    • Added skipInvalidation option to mutation APIs for finer cache control.
    • Enhanced cache invalidation to use predicate-based matching (broader route-prefix invalidation).
    • Added Angular client snapshots with multi-content accept support, filtered query-params handling, and new model types for tests.
  • Bug Fixes

    • Fixed relative import path generation in split-tags mode so imports point to tag subdirectories.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9bec046e-7c1f-436a-9933-f77877d1f6c2

📥 Commits

Reviewing files that changed from the base of the PR and between dae7f48 and d9693d0.

📒 Files selected for processing (3)
  • packages/query/src/mutation-generator.ts
  • samples/angular-query/__snapshots__/api/endpoints/pets/pets.ts
  • samples/angular-query/src/api/endpoints/pets/pets.ts

📝 Walkthrough

Walkthrough

Transforms import paths for tags-split implementation files and makes mutation invalidation generation OpenAPI-spec-aware: when no explicit params are provided it tries to derive a route prefix from the spec to emit predicate-based invalidation; adds/updates multiple React Query and Angular generated snapshot files.

Changes

Cohort / File(s) Summary
Import Path Rewriting
packages/core/src/writers/split-tags-mode.ts
Adjusts imports passed to the builder for implementation files in tags-split mode: strips leading ./ and rewrites local import paths to be relative to the tag subdirectory (e.g., ../<tagRef>/<basename>). Mock import flow unchanged.
Mutation Invalidation Logic
packages/query/src/mutation-generator.ts
generateInvalidateCall now accepts an optional spec and, when target.params is absent, looks up the operation by operationId to emit either a predicate-based invalidation using a computed route prefix (if required path params exist) or falls back to calling the query-key function with no args; generateMutationHook wiring updated to pass context.spec.
React Query snapshots & config
tests/__snapshots__/react-query/invalidates/endpoints.ts, tests/configs/react-query.config.ts
Snapshots updated to use predicate-based invalidation and to accept/propagate queryClient and skipInvalidation where needed; config mapping adjusted (expanded/narrowed targets, removed uploadFile mapping).
Angular snapshot additions (service & models)
tests/__snapshots__/angular/multi-content-query-params/endpoints.ts, tests/__snapshots__/angular/multi-content-query-params/model/*.ts
Adds generated Angular test service AngularMultiContentQueryParamsTestService, ListItemsAccept, and model files (error, item, items, listItemsParams, model/index) supporting multi-content query params snapshot.
Sample updates
samples/angular-query/.../api/endpoints/pets/pets.ts, samples/angular-query/src/api/endpoints/pets/pets.ts
Replaced list-key invalidations with predicate-based invalidation that matches queries whose first queryKey element string-starts-with /v, across multiple pet mutation handlers.

Sequence Diagram(s)

sequenceDiagram
  participant Caller as Mutation Caller
  participant M as Generated onSuccess
  participant G as generateInvalidateCall
  participant S as OpenAPI Spec
  participant Q as QueryClient

  Caller->>M: mutation succeeds (data, variables, context)
  M->>G: call generateInvalidateCall(target, context.spec)
  alt target.params present
    G->>Q: invalidateQueries({ queryKey: get-<query>-query-key(generatedArgs) })
  else target.params absent
    G->>S: lookup operation by operationId
    alt operation has required path params
      G->>Q: invalidateQueries({ predicate: q => q.queryKey[0] startsWith("/derived/prefix") })
    else
      G->>Q: invalidateQueries({ queryKey: get-<query>-query-key() })
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • #3051 — Overlapping edits to packages/core/src/writers/split-tags-mode.ts (import path handling).
  • #2928 — Related changes touching packages/query/src/mutation-generator.ts and mutation-hook wiring.
  • #3082 — Edits in packages/query/src/mutation-generator.ts impacting invalidate logic and associated types.

Suggested reviewers

  • melloware
  • snebjorn

Poem

🐇 I hopped through code and found the trail,
I nudged the imports so no paths fail.
I sniffed the spec and learned the route,
I nudged the cache so queries boot.
Hooray — small hops, a tidy rail.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main bugs being fixed: missing args in mutationInvalidates and incorrect import paths in tags-split mode.
Linked Issues check ✅ Passed All coding objectives from issue #3090 are met: predicate-based invalidation for queries without params mapping [#3090], corrected import paths in tags-split mode [#3090], and proper handling of queries with explicit params mappings.
Out of Scope Changes check ✅ Passed All changes directly address the two bugs in #3090. Angular multi-content test files are generated artifacts for testing the core fixes and are within scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/query/src/mutation-generator.ts`:
- Around line 104-109: getRoutePrefix currently collapses templates with no
static prefix to '/' and the matching logic uses startsWith which matches
siblings like '/pets-stats'; update getRoutePrefix to return an empty string
when there is no static prefix (i.e., when prefix === '' after trimming the
trailing slash) instead of returning '/', and change the predicate used where
routes are matched (the same logic around lines 129-132) so it is path-segment
aware: if a static prefix is non-empty, require that the candidate route either
equals the prefix or startsWith(prefix + '/') (or prefix followed by
end-of-string), and if the prefix is empty treat it as “no static prefix” (do
not default to '/' and do not broadly match). Ensure you reference and update
getRoutePrefix and the matching code near lines 129-132 accordingly.
- Around line 129-136: The code currently treats a missing route (route ===
undefined) as safe for zero-arg invalidation; change the logic so you only emit
the zero-argument form when you have positively confirmed the route exists and
does NOT include path params. Concretely, update the conditional around
findOperationRoute(spec, target.query) to require route && !route.includes('{')
before returning the `queryClient.${method}({ queryKey: ${queryKeyFn}() })`
string; for the else branch (route missing or contains '{') keep or emit the
predicate-based invalidation (the existing `predicate: (query) => { const key =
query.queryKey[0]; return typeof key === 'string' &&
key.startsWith('${prefix}'); }`) or another conservative form so you don't
assume zero-arg safety. Reference symbols: findOperationRoute, getRoutePrefix,
route, queryKeyFn, method, and the queryClient invalidation string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a85a4dd7-69ed-4ba6-9123-b1ad226058e2

📥 Commits

Reviewing files that changed from the base of the PR and between b52e2cb and 67cdcb2.

📒 Files selected for processing (4)
  • packages/core/src/writers/split-tags-mode.ts
  • packages/query/src/mutation-generator.ts
  • tests/__snapshots__/react-query/invalidates/endpoints.ts
  • tests/configs/react-query.config.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
packages/query/src/mutation-generator.ts (2)

133-140: ⚠️ Potential issue | 🟠 Major

Don't treat a missed route lookup as safe for zero-arg invalidation.

Line 140 assumes findOperationRoute() returning undefined means the target query has no required args, but lookup can miss whenever context.spec is absent or operationId resolution fails. That recreates the original TS/runtime bug; only emit queryKeyFn() after a positive route && !route.includes('{') check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query/src/mutation-generator.ts` around lines 133 - 140, The current
logic assumes findOperationRoute(spec, target.query) returning undefined means
the route has no path params and emits queryKeyFn(), which is unsafe; change the
branch so you only emit `queryClient.<method>({ queryKey: ${queryKeyFn}() })`
when there is a positive route AND the route does not include '{' (i.e., `route
&& !route.includes('{')`); for all other cases (route undefined or route
includes '{') keep emitting the predicate-based invalidation that inspects
query.queryKey[0] (the existing predicate using getRoutePrefix(route) when route
is present, and a safe fallback predicate when route is missing) so you never
treat a missed lookup as a zero-arg invalidation.

109-115: ⚠️ Potential issue | 🟠 Major

Make the broad predicate path-segment aware.

Line 114 turns routes with no leading static segment into '/', and Line 136 then matches almost the entire cache. Even with a non-empty prefix, startsWith('/pets') also catches siblings like /pets-stats; this should only match key === prefix or key.startsWith(prefix + '/'), and an empty prefix should stay "no safe broad match."

Also applies to: 134-136

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query/src/mutation-generator.ts` around lines 109 - 115,
getRoutePrefix currently returns '/' for routes with no leading static segment,
and the broad-match logic uses naive startsWith checks so keys like
'/pets-stats' incorrectly match; change getRoutePrefix to return an empty string
for no static prefix (do not coerce to '/'), and update the broad predicate that
uses prefix (the code that checks key === prefix or key.startsWith(prefix +
'/')) so it only matches when key equals the prefix or begins with the prefix
plus a slash; also ensure that when prefix is empty you do not generate/allow a
broad match (treat empty prefix as "no safe broad match").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/query/src/mutation-generator.ts`:
- Around line 125-127: The code treats empty params mappings (e.g., params: []
or params: {}) as present because it only checks truthiness; update the guard
around target.params in mutation-generator.ts so empty arrays or empty objects
are treated as "no mapping" — replace the simple if (target.params) with a check
that ensures target.params is non-empty (e.g., target.params &&
!(Array.isArray(target.params) && target.params.length === 0) && !(typeof
target.params === 'object' && !Array.isArray(target.params) &&
Object.keys(target.params).length === 0)) before calling
generateParamArgs(target.params) and emitting queryClient.${method}({ queryKey:
${queryKeyFn}(${args}) }); so generateParamArgs/run of ${queryKeyFn} is only
used for truly non-empty mappings.

In `@tests/__snapshots__/angular/multi-content-query-params/endpoints.ts`:
- Around line 20-25: The params merging currently spreads options?.params
directly, which drops query params when a HttpParams instance is passed; change
the merge to mirror the headers fix: detect if options?.params instanceof
HttpParams and, if so, use it directly (or extract its serialized form) instead
of spreading, otherwise spread the plain object (i.e., replace the direct spread
of options?.params with a conditional based on HttpParams). Update the code that
builds query params where HttpClientOptions.params and options?.params are used
(the same area that handles headers) to handle both HttpParams and plain objects
consistently.

---

Duplicate comments:
In `@packages/query/src/mutation-generator.ts`:
- Around line 133-140: The current logic assumes findOperationRoute(spec,
target.query) returning undefined means the route has no path params and emits
queryKeyFn(), which is unsafe; change the branch so you only emit
`queryClient.<method>({ queryKey: ${queryKeyFn}() })` when there is a positive
route AND the route does not include '{' (i.e., `route &&
!route.includes('{')`); for all other cases (route undefined or route includes
'{') keep emitting the predicate-based invalidation that inspects
query.queryKey[0] (the existing predicate using getRoutePrefix(route) when route
is present, and a safe fallback predicate when route is missing) so you never
treat a missed lookup as a zero-arg invalidation.
- Around line 109-115: getRoutePrefix currently returns '/' for routes with no
leading static segment, and the broad-match logic uses naive startsWith checks
so keys like '/pets-stats' incorrectly match; change getRoutePrefix to return an
empty string for no static prefix (do not coerce to '/'), and update the broad
predicate that uses prefix (the code that checks key === prefix or
key.startsWith(prefix + '/')) so it only matches when key equals the prefix or
begins with the prefix plus a slash; also ensure that when prefix is empty you
do not generate/allow a broad match (treat empty prefix as "no safe broad
match").
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8eb0bd69-44fa-422a-ad25-68c850ee9bd2

📥 Commits

Reviewing files that changed from the base of the PR and between 67cdcb2 and 9ef9ced.

📒 Files selected for processing (7)
  • packages/query/src/mutation-generator.ts
  • tests/__snapshots__/angular/multi-content-query-params/endpoints.ts
  • tests/__snapshots__/angular/multi-content-query-params/model/error.ts
  • tests/__snapshots__/angular/multi-content-query-params/model/index.ts
  • tests/__snapshots__/angular/multi-content-query-params/model/item.ts
  • tests/__snapshots__/angular/multi-content-query-params/model/items.ts
  • tests/__snapshots__/angular/multi-content-query-params/model/listItemsParams.ts
✅ Files skipped from review due to trivial changes (5)
  • tests/snapshots/angular/multi-content-query-params/model/listItemsParams.ts
  • tests/snapshots/angular/multi-content-query-params/model/item.ts
  • tests/snapshots/angular/multi-content-query-params/model/items.ts
  • tests/snapshots/angular/multi-content-query-params/model/error.ts
  • tests/snapshots/angular/multi-content-query-params/model/index.ts

@melloware melloware added the tanstack-query TanStack Query related issue label Apr 4, 2026
@melloware
Copy link
Copy Markdown
Collaborator

good CodeRabbit feedback and build did not pass.

@zeriong zeriong closed this Apr 4, 2026
@zeriong zeriong force-pushed the fix/mutation-invalidates-codegen-bugs-3090 branch from d9693d0 to b52e2cb Compare April 4, 2026 15:22
@zeriong zeriong deleted the fix/mutation-invalidates-codegen-bugs-3090 branch April 4, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tanstack-query TanStack Query related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: mutationInvalidates generates broken code with react-query + tags-split mode

2 participants