JITSU-137 feat(console): browse events of all connections, show batch cost - #1432
JITSU-137 feat(console): browse events of all connections, show batch cost#1432vklimontovich wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Reviewed the new all-actors events-log flow and related UI updates (API routes, actor scoping, and table rendering). I found two correctness/security-risk areas worth tightening: (1) actor-id scoping in workspaceActorIds is broader than the requested log type, and (2) workspace slug inputs can pass access checks but still produce empty actor scopes because downstream lookups keep using the unresolved workspaceId string.
Data view changes on the "API Destinations & Functions Logs" and
"Batches & Data Warehouse Events" tabs:
- the connection selector is now optional. "All Connections" is the first
option and the default, and an extra "Connection" column (linked, both
sides trimmed in the middle) tells the rows apart
- batches show the cost the data warehouse charges for the load, from
state.statistics.estimatedCost. Only BigQuery reports a non-zero value
today, so a zero renders as an empty cell. The (?) in the header spells
out that it is an estimate and the invoice may differ
events_log has no workspaceId column - it is scoped by actorId - so the
"all actors" query resolves the actor ids the workspace owns and filters
by them, otherwise rows of other workspaces would leak. Rows now carry
actorId, and the ids are scoped to the actor kinds that emit the
requested type.
Rather than passing a magic "all" as a path segment, no actor is now no
path segment: GET /api/:workspaceId/log/:type. Both that route and
/log/:type/:actorId are thin wrappers over streamEventsLog() in
lib/server/events-log-stream.ts.
ConnectionTitle / ProfileBuilderTitle take a truncationPolicy prop
("none" by default, so every existing call site is unchanged).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review follow-ups on the all-actors events log: - the "all actors" filter included every actor kind for every non-incoming type. Now each type gets only the kinds that actually emit it: `function` is written by rotor with the link id (or the profile builder id, see builder.ts), `bulker_*` with bulker's destinationId - a link id, or a destination id when the profile builder writes its result. An unknown type still gets every kind, since narrower would silently hide rows - the route takes a workspace slug as well as an id: verifyAccess resolves a slug internally, but every lookup here matches on workspaceId, so a slug produced a false-empty response (and a 403 on the single-actor path). Resolve it to the canonical id once, up front - selecting "All Connections" left the "debug logging is enabled" banner up, since debugEnabled was never cleared when the selected actor is not a connection Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2323abb to
2f5a9eb
Compare
There was a problem hiding this comment.
Reviewed the changes to the events-log API split ([actorId] + index) and the Events Browser updates for all-actors mode/cost column. I found one actionable issue around single-actor ownership validation that still seems too permissive for type-specific actor IDs.
| } | ||
| return; | ||
| } | ||
| const [link, pb, dst] = await Promise.all([ |
There was a problem hiding this comment.
Possible bug: this single-actor validation is still broader than the per-type emitter rules below. For example, type="function" currently accepts destination IDs via dst, even though function logs are keyed by link/profile-builder IDs. That widens the allowed actor-id set and reintroduces the collision risk you already constrained in workspaceActorIds(). Should this check be type-scoped (same actorKinds(type) logic) so single-actor and all-actors paths enforce the same actor kinds?
JITSU-137Two asks on the Data view, both on the API Destinations & Functions Logs and Batches & Data Warehouse Events tabs.
Connection selector is optional
"All Connections" is now the first option and the default. When it's selected, an extra Connection column identifies each row — linked to the connection editor, with both sides of the title trimmed in the middle so neither the source nor the destination gets cut off at an arbitrary spot. Full names are in the tooltip.
ConnectionTitle/ProfileBuilderTitlegrew atruncationPolicy?: "none" | numberprop for this."none"is the default, so every existing call site renders exactly as before.Estimated cost on batches
New column fed by
state.statistics.estimatedCost(bulker/bulkerlib/bulker.go). The(?)in the header says it's an estimate of what the warehouse charges and that the invoice may differ due to discounts, committed-use pricing, free tiers.Formatting: max 4 decimals,
< $0.0001below that (most batches land around5.9e-05, which would otherwise round to$0.0001and read as nearly double), and an empty cell for zero — only BigQuery reports a non-zero cost today, so a column of$0would be pure noise. The exact unrounded value stays in the tooltip.Server side
events_loghas noworkspaceIdcolumn — it's scoped byactorIdonly. The "all actors" query therefore resolves the actor ids the workspace owns and filtersactorId in (...), or rows of other workspaces would leak. The ids are scoped to the actor kinds that emit the requested type (streams forincoming; connections / destinations / profile builders forfunctionandbulker_*), so unrelated ids and cross-table id collisions can't widen the result set. Rows now carryactorId.Rather than passing a magic
allas a path segment, no actor is no path segment:GET /api/:workspaceId/log/:type. That route and/log/:type/:actorIdare both thin wrappers overstreamEventsLog()inlib/server/events-log-stream.ts, which holds the ownership checks, the query and the gzip streaming that used to live in the route handler.Testing
Ran locally against prod Postgres + prod ClickHouse:
/log/bulker_batch(no actor)actorId/log/bulker_batch/<actorId>/log/function(no actor)actorIdCosts came back real —
5.9e-05→< $0.0001,0.41934555145→$0.4193,0→ empty.tsc --noEmit,eslint, andprettier --checkare clean on the console.vitest --project unit: 25 tests pass;mcp-config-schema.test.tscrashes its worker locally, which is pre-existing — it imports onlylib/schema/json-schema, untouched here.🤖 Generated with Claude Code