Skip to content

fix: economicResources.pageInfo.totalCount ignored pagination limit - #69

Merged
phoebus-84 merged 1 commit into
masterfrom
fix/economic-resources-total-count
Sep 4, 2026
Merged

phoebus-84 merged 1 commit into
masterfrom
fix/economic-resources-total-count

Conversation

@phoebus-84

Copy link
Copy Markdown
Collaborator

Summary

Zenflows.GQL.Connection.from_list/2 computes total_count by counting the records actually fetched for the current page (bounded by first/last, plus the +1 peek row used to detect hasNextPage) — not a real count of everything matching the filter. That's deliberate, tested behavior for the generic Connection/Page pagination shared by every connection in the schema (see test/db/page.test.exs, which has a whole comment block documenting "totalCount depends on num"). But it means economicResources' totalCount silently caps at whatever page size the client asked for: requesting last: 12 always reports totalCount: 12, even when far more rows match — and hasNextPage: true in the same response proves it.

Verified live against https://proxy.dpp-dev.ddns.dyne.org/zenflows/api:

last: 3   → totalCount: 3,   hasNextPage: true
last: 100 → totalCount: 100, hasNextPage: true   (still more pages after 100!)

This is a real discrepancy against the field's own documented contract (@desc "The total result count, if it can be determined.") for a type where the count is client-visible product/service/design inventory totals (interfacer-gui's "Showing X results").

Fix

Mirrors the existing count_distinct_primary_accountable/1 pattern (EconomicResource.Query.filtered_query/1 + an unbounded aggregate query):

  • EconomicResource.Query.count/1 — new, unbounded COUNT(*) over the filtered query
  • EconomicResource.Domain.count/1 — thin wrapper
  • EconomicResource.Resolv.economic_resources/2 — overrides page_info.total_count with it, the same way distinct_primary_accountable_count is already added

Scoped to economic_resources only — this is not a change to the shared Connection module, so every other paginated type (agents, proposals, etc.) keeps its existing, tested, page-bound behavior. Changing that generically would touch ~25 other resolvers and contradict the existing test/db/page.test.exs contract; only economic_resources gets the extra accuracy, matching how it already gets the extra distinctPrimaryAccountableCount query.

Testing

Set up a full local toolchain to actually run this (Elixir 1.14.5/OTP 25 via mise, throwaway Postgres in Docker):

  • New EconomicResource.Domain "count/1" test and a GQL-level EconomicResource.Type test that requests first: 2 against 5 matching resources and asserts totalCount == 5 (not 2) while hasNextPage == true and distinctPrimaryAccountableCount == 1 — reproduces the exact bug shape, both pass.
  • Full suite: mix test → 568 tests, 6 failures. Confirmed by stashing and re-running that the same 6 failures reproduce identically on unmodified master (artifacts of the throwaway ADMIN_KEY/ROOM_SALT test env, plus one pre-existing unrelated nil-classified_as bug in classifications/1) — zero regressions from this change.
  • mix credo — clean on the touched files.

🤖 Generated with Claude Code

Zenflows.GQL.Connection.from_list/2 computes total_count by counting
the records actually fetched for the current page (bounded by
first/last, plus the +1 peek row used to detect hasNextPage) — not a
real count of everything matching the filter. That's deliberate,
tested behavior for the generic Connection/Page pagination shared by
every connection in the schema (see test/db/page.test.exs), but it
means economicResources' totalCount silently caps at whatever page
size the client asked for: requesting `last: 12` always reports
totalCount: 12, even when far more rows match, and hasNextPage: true
in the same response proves it.

This is a real discrepancy against the field's own documented
contract ("The total result count, if it can be determined.") for a
type where the count is client-visible product/service/design
inventory totals.

Fix: mirror the existing count_distinct_primary_accountable/1 pattern
(EconomicResource.Query.filtered_query/1 + an unbounded aggregate
query) with a new EconomicResource.Query.count/1 /
EconomicResource.Domain.count/1, and have
EconomicResource.Resolv.economic_resources/2 override
page_info.total_count with it — same way distinct_primary_accountable_count
is already added. Scoped to economic_resources only, not a change to
the shared Connection module, so every other paginated type (agents,
proposals, etc.) keeps its existing, tested, page-bound behavior.

Tests: new EconomicResource.Domain "count/1" test and a GQL-level
EconomicResource.Type test that requests first: 2 against 5 matching
resources and asserts totalCount == 5 (not 2) while
distinctPrimaryAccountableCount == 1. Ran the full suite locally
(mix test, 568 tests) against a throwaway Postgres container: 6
pre-existing failures (env-config artifacts of the throwaway
ADMIN_KEY/ROOM_SALT setup, plus one unrelated nil-classified_as bug
in classifications/1) reproduce identically on unmodified master;
zero regressions from this change. mix credo also clean for the
touched files.
@phoebus-84
phoebus-84 merged commit a8ba830 into master Sep 4, 2026
1 check failed
phoebus-84 added a commit to interfacerproject/interfacer-gui that referenced this pull request Sep 4, 2026
## Summary

Picks up
[`interfacer-client#1`](interfacerproject/interfacer-client#1),
which adds `distinctPrimaryAccountableCount` to `FETCH_RESOURCES`'s
`pageInfo` selection. Combined with
[`zenflows#69`](interfacerproject/zenflows#69)
(fixes `economicResources.pageInfo.totalCount` to reflect the real
filtered total instead of just the page size), this fixes the catalogue
pages' hero stats:

| | before | after |
|---|---|---|
| Total Products | 12 (= page size requested) | **121** (real total) |
| Manufacturers | 0 | **2** (real distinct count) |

## Changes

- `package.json`: `@dyne/interfacer-client` `^0.6.0` → `^0.6.1`
- `pnpm-lock.yaml`: updated accordingly

No source code changes — dependency bump only.

## Testing

- `pnpm check-types` — clean
- `pnpm check-format` — clean
- Verified live: restarted the dev server against the bumped dependency
and confirmed `/products` now shows "121 Total Products" / "2
Manufacturers" (previously "12" / "0"), with the real GraphQL response
now carrying the fixed fields end-to-end from `zenflows` →
`interfacer-client` → `interfacer-gui`.

## Related

- #893 (catalogue heading rework — independent, this PR is branched off
`main`, not off that branch)
- interfacerproject/interfacer-client#1
- interfacerproject/zenflows#69

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: euripide <euripide@MacBook-Air-di-euripide.local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant