Skip to content

JITSU-137 feat(console): browse events of all connections, show batch cost - #1432

Open
vklimontovich wants to merge 2 commits into
newjitsufrom
feat/console-events-log-all-connections
Open

JITSU-137 feat(console): browse events of all connections, show batch cost#1432
vklimontovich wants to merge 2 commits into
newjitsufrom
feat/console-events-log-all-connections

Conversation

@vklimontovich

@vklimontovich vklimontovich commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

JITSU-137

Two 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 / ProfileBuilderTitle grew a truncationPolicy?: "none" | number prop 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.0001 below that (most batches land around 5.9e-05, which would otherwise round to $0.0001 and read as nearly double), and an empty cell for zero — only BigQuery reports a non-zero cost today, so a column of $0 would be pure noise. The exact unrounded value stays in the tooltip.

Server side

events_log has no workspaceId column — it's scoped by actorId only. The "all actors" query therefore resolves the actor ids the workspace owns and filters actorId in (...), or rows of other workspaces would leak. The ids are scoped to the actor kinds that emit the requested type (streams for incoming; connections / destinations / profile builders for function and bulker_*), so unrelated ids and cross-table id collisions can't widen the result set. Rows now carry actorId.

Rather than passing a magic all as a path segment, no actor is no path segment: GET /api/:workspaceId/log/:type. That route and /log/:type/:actorId are both thin wrappers over streamEventsLog() in lib/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:

request result
/log/bulker_batch (no actor) records from 3 different connections, each carrying actorId
/log/bulker_batch/<actorId> single connection, unchanged behaviour
/log/function (no actor) records with actorId

Costs came back real — 5.9e-05< $0.0001, 0.41934555145$0.4193, 0 → empty.

tsc --noEmit, eslint, and prettier --check are clean on the console. vitest --project unit: 25 tests pass; mcp-config-schema.test.ts crashes its worker locally, which is pre-existing — it imports only lib/schema/json-schema, untouched here.

🤖 Generated with Claude Code

@vklimontovich vklimontovich added the deploy:console Auto-deploy console to beta when this PR merges (JITSU-68) label Jul 28, 2026
@vklimontovich vklimontovich changed the title feat(console): browse events of all connections, show batch cost JITSU-137 feat(console): browse events of all connections, show batch cost Jul 28, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread webapps/console/lib/server/events-log-stream.ts Outdated
Comment thread webapps/console/lib/server/events-log-stream.ts
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Jul 28, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed the new all-actors log flow end-to-end (UI selector changes, API routing split, and server-side workspace scoping for events_log). I found one user-visible regression in function logs and left an inline comment with details.

Comment thread webapps/console/components/DataView/EventsBrowser.tsx
vklimontovich and others added 2 commits July 28, 2026 14:11
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>

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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([

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deploy:console Auto-deploy console to beta when this PR merges (JITSU-68)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant