Skip to content

refactor(events): consolidate wallet event list query logic (#1271) - #1383

Open
Rafiat30 wants to merge 1 commit into
LabsCrypt:mainfrom
Rafiat30:fix/consolidate-wallet-event-list
Open

refactor(events): consolidate wallet event list query logic (#1271)#1383
Rafiat30 wants to merge 1 commit into
LabsCrypt:mainfrom
Rafiat30:fix/consolidate-wallet-event-list

Conversation

@Rafiat30

Copy link
Copy Markdown

Closes #1271

Summary

Two near-identical implementations of "paginated stream events by sender/recipient wallet" existed with different feature sets:

  • GET /v1/events (backend/src/routes/v1/events.routes.ts) supported comma-separated type filtering but never included the related stream.
  • GET /v1/users/:publicKey/events (backend/src/controllers/user.controller.ts) always included the related stream but had no type filter.

Both duplicated the same sender/recipient OR where-clause, pagination (limit/offset/page) logic, and event-type regex/validation.

This PR extracts a single shared listEventsForWallet helper (plus supporting parseEventTypeFilter, resolveEventsPageSize, resolveEventsOffset) into a new repository module, and updates both endpoints to delegate to it, so both now support the union of the two original feature sets.

New files

  • backend/src/repositories/streamEvent.repository.ts — shared query logic:
    • EVENT_TYPES, MAX_EVENTS_PAGE_SIZE, DEFAULT_EVENTS_PAGE_SIZE
    • parseEventTypeFilter(rawType) — parses/validates a comma-separated type query param
    • resolveEventsPageSize(rawLimit) — clamps/defaults limit
    • resolveEventsOffset({ rawOffset, rawPage, limit }) — resolves offset from offset or page
    • listEventsForWallet({ address, types, limit, offset, includeStream }) — builds the sender/recipient OR where-clause, applies the optional eventType in filter, paginates, optionally includes the related stream, and returns { events, total, limit, offset, hasMore }
  • backend/tests/streamEvent.repository.test.ts — unit tests for the new repository module

Modified files

  • backend/src/routes/v1/events.routes.tsGET /v1/events now delegates to listEventsForWallet; gained a new includeStream query param (default false, matching its historical behavior of never embedding the stream)
  • backend/src/controllers/user.controller.tsGET /v1/users/:publicKey/events now delegates to listEventsForWallet; gained comma-separated type filtering (previously unsupported); includeStream defaults to true here to preserve this endpoint's historical behavior of always embedding the stream
  • backend/src/controllers/stream.controller.ts — updated to import DEFAULT_EVENTS_PAGE_SIZE / MAX_EVENTS_PAGE_SIZE from the new repository module instead of events.routes.ts, since those constants moved
  • backend/tests/integration/events-list.test.ts — added includeStream coverage for GET /v1/events, plus a new "filtering parity" describe block asserting both endpoints build identical where clauses (sender/recipient OR, and eventType in) via the shared helper, and that both reject an all-invalid type filter with 400
  • backend/tests/user.controller.test.ts — added coverage for includeStream default/opt-out and the new type filter (including the 400 rejection path) on GET /v1/users/:publicKey/events

Implementation details

  • Union of features: listEventsForWallet accepts types (comma-separated event type filter) and includeStream (whether to include: { stream: true }), so either caller can opt into either feature.
  • Backward compatibility: each endpoint keeps its own historical default for includeStream (false for /v1/events, true for /v1/users/:publicKey/events), so existing consumers (e.g. the frontend Activity page, which reads data.events from /v1/events and never asked for includeStream) see no response-shape change. Response envelopes ({ events, total, limit, offset, hasMore } vs { data, total, hasMore, limit, offset }) are unchanged — only the underlying query logic is shared.
  • Identical filtering semantics: both endpoints now use the exact same parseEventTypeFilter (case-insensitive, comma-separated, unknown values dropped from the applied filter but tracked separately so an all-invalid filter still returns 400) and the same pagination resolution (resolveEventsPageSize, resolveEventsOffset).

Tests added

  • backend/tests/streamEvent.repository.test.ts (new): unit tests for parseEventTypeFilter, resolveEventsPageSize, resolveEventsOffset, and listEventsForWallet (where-clause construction, type filter application/omission, pagination, includeStream on/off, hasMore computation).
  • backend/tests/user.controller.test.ts: includeStream default-on / includeStream=false opt-out, comma-separated type filter forwarding, and 400 on an all-invalid type filter.
  • backend/tests/integration/events-list.test.ts: includeStream default-off / includeStream=true opt-in for GET /v1/events, plus a new parity suite that hits both GET /v1/events and GET /v1/users/:publicKey/events with the same wallet/type filter and asserts the Prisma where clauses passed to findMany/count are identical, and that both return 400 for an all-invalid type filter.

How to test manually

Start the backend (npm run dev in backend/) with a valid session token, then:

# GET /v1/events — comma-separated type filter, includeStream opt-in
curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:3000/v1/events?address=$WALLET&type=PAUSED,RESUMED&includeStream=true&limit=10"

# GET /v1/events — default (no stream embedded)
curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:3000/v1/events?address=$WALLET&type=PAUSED,RESUMED&limit=10"

# GET /v1/users/:publicKey/events — same type filter (previously unsupported here), default includes stream
curl "http://localhost:3000/v1/users/$WALLET/events?type=PAUSED,RESUMED&limit=10"

# GET /v1/users/:publicKey/events — opt out of the (now-default) stream embed
curl "http://localhost:3000/v1/users/$WALLET/events?type=PAUSED,RESUMED&includeStream=false&limit=10"

# Both should reject an all-invalid type filter with 400
curl -i -H "Authorization: Bearer $TOKEN" "http://localhost:3000/v1/events?address=$WALLET&type=BOGUS"
curl -i "http://localhost:3000/v1/users/$WALLET/events?type=BOGUS"

Expect: the type filter behaves identically on both endpoints (same accepted values, same 400 on an all-invalid filter), and includeStream toggles the embedded stream object on both endpoints identically, with each endpoint's response envelope unchanged from before ({ events, total, limit, offset, hasMore } for /v1/events, { data, total, hasMore, limit, offset } for /v1/users/:publicKey/events).

Test/build status

  • npx vitest run --no-coverage tests/streamEvent.repository.test.ts tests/user.controller.test.ts tests/integration/events-list.test.ts → 50/50 passed
  • npx vitest run --no-coverage --exclude='tests/integration/**' (full unit suite) → 339 passed, 3 skipped (pre-existing, unrelated), 0 failed
  • npx tsc --noEmit → no errors
  • Real-DB integration tests under backend/tests/integration/ (e.g. stream-lifecycle.test.ts) were not run in this environment since no DATABASE_URL/Postgres was available; those tests self-skip gracefully by design (see backend/tests/integration/_db.ts, issue [BUG] Backend integration tests fail on main due to PostgreSQL connection error #760) rather than failing. events-list.test.ts, which is what covers this change, fully mocks Prisma and ran/passed normally.

…t#1271)

Extract the duplicated "paginated stream events by sender/recipient
wallet" query logic from GET /v1/events (events.routes.ts) and
GET /v1/users/:publicKey/events (user.controller.ts) into a single
listEventsForWallet helper in the new
backend/src/repositories/streamEvent.repository.ts.

The shared repository also exposes parseEventTypeFilter,
resolveEventsPageSize, and resolveEventsOffset, and now supports the
union of both endpoints' prior feature sets: comma-separated
`type` filtering (previously only on /v1/events) and an optional
includeStream flag that embeds the related stream (previously
always-on and only on /v1/users/:publicKey/events). Each endpoint
keeps its historical includeStream default (false for /v1/events,
true for /v1/users/:publicKey/events) so existing consumers see no
response-shape change.

stream.controller.ts is updated to import DEFAULT_EVENTS_PAGE_SIZE /
MAX_EVENTS_PAGE_SIZE from the new repository module instead of from
events.routes.ts, where they no longer live.

Adds streamEvent.repository.test.ts covering the new helpers, and
extends user.controller.test.ts and events-list.test.ts with
includeStream/type-filter coverage plus parity tests asserting both
endpoints build identical where-clauses via the shared helper.
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.

[Audit] Duplicated "list events for a wallet" implementations with divergent feature sets

2 participants