mirror of
https://github.com/coder/coder.git
synced 2026-10-28 02:42:54 +08:00
feat: wire up Template Builder session telemetry endpoint (#27124)
`TemplateBuilderSession` telemetry types and telemetry-server ingestion were added in earlier PRs (#25082, coder/coder-telemetry-server#41), but no code ever produced session events. This adds the missing producer. **Backend**: `POST /api/v2/templatebuilder/sessions` reports wizard entry and compose completion events directly via `api.Telemetry.Report()`, using the same inline pattern as `NetworkEvents` and `UserTailnetConnections`. No database migration or `createSnapshot()` changes needed. RBAC requires `policy.ActionCreate` on `ResourceTemplate.AnyOrganization()`, matching the compose endpoint. **Frontend**: The template builder wizard fires `wizard_entry` on page mount and `compose_completion` on create success or failure. A client-generated session ID (UUID) correlates the two events for the same wizard visit, enabling precise funnel analysis and abandonment detection in BigQuery. Duration is tracked via `Date.now()` in the wizard state. Closes https://linear.app/codercom/issue/DEVEX-599 <details> <summary>Implementation plan</summary> ## Root Cause Analysis The DEVEX-599 ticket diagnosis suggested missing DB tables, queries, and `eg.Go` blocks. That diagnosis assumes the DB-backed periodic snapshot path is required. It is not. Investigation shows two telemetry reporting patterns in the codebase: 1. **DB-backed periodic snapshots** (`createSnapshot()` with `eg.Go` blocks): Used for durable entities like workspaces, templates, users. 2. **Direct inline reporting** (`api.Telemetry.Report(&telemetry.Snapshot{...})`): Used for ephemeral events like `NetworkEvents`, `UserTailnetConnections`, `CLIInvocations`. Template builder sessions are ephemeral events, so the direct inline reporting pattern is the correct fit. ## Backend Changes - `codersdk/templatebuilder.go`: `TemplateBuilderSessionRequest` type with `SessionID`, `EventType` enum, `TemplateBuilderSession()` client method - `coderd/coderd.go`: Route registration in `/templatebuilder` group - `coderd/templatebuilder_handler.go`: Handler with RBAC check, request validation, session ID fallback, and inline telemetry report - `coderd/templatebuilder_handler_test.go`: Tests for wizard entry, compose completion, invalid event type, disabled feature, and member RBAC rejection ## Frontend Changes - `site/src/api/api.ts`: `recordTemplateBuilderSession` API method - `site/src/api/queries/templateBuilder.ts`: React Query mutation - `site/src/pages/TemplateBuilder/wizardState.ts`: `sessionId` and `enteredAt` fields, `createWizardState()` factory for per-mount initialization - `site/src/pages/TemplateBuilder/TemplateBuilderPageView.tsx`: `sessionId` prop, `useReducer` initializer form - `site/src/pages/TemplateBuilder/TemplateBuilderPage.tsx`: Telemetry calls for wizard entry (on mount) and compose completion (on create success/failure) </details> > 🤖 Generated by Coder Agents --------- Co-authored-by: Coder Agent <agent@coder.com>
This commit is contained in:
co-authored by
Coder Agent
parent
daf655dff8
commit
51ac968d5a
@@ -150,6 +150,40 @@ type TemplateBuilderCreateTemplateResponse struct {
|
||||
Template Template `json:"template"`
|
||||
}
|
||||
|
||||
// TemplateBuilderSessionEventType enumerates the event types for
|
||||
// template builder session telemetry.
|
||||
type TemplateBuilderSessionEventType string
|
||||
|
||||
const (
|
||||
TemplateBuilderSessionEventWizardEntry TemplateBuilderSessionEventType = "wizard_entry"
|
||||
TemplateBuilderSessionEventComposeCompletion TemplateBuilderSessionEventType = "compose_completion"
|
||||
)
|
||||
|
||||
// TemplateBuilderSessionRequest is the request body for
|
||||
// POST /api/v2/templatebuilder/sessions.
|
||||
type TemplateBuilderSessionRequest struct {
|
||||
SessionID uuid.UUID `json:"session_id" format:"uuid" validate:"required"`
|
||||
EventType TemplateBuilderSessionEventType `json:"event_type" validate:"required,oneof=wizard_entry compose_completion"`
|
||||
BaseTemplateID string `json:"base_template_id,omitempty"`
|
||||
ModuleIDs []string `json:"module_ids,omitempty"`
|
||||
DurationSeconds float64 `json:"duration_seconds,omitempty"`
|
||||
Success bool `json:"success,omitempty"`
|
||||
}
|
||||
|
||||
// TemplateBuilderSession reports a template builder session event for
|
||||
// telemetry purposes.
|
||||
func (c *Client) TemplateBuilderSession(ctx context.Context, req TemplateBuilderSessionRequest) error {
|
||||
res, err := c.Request(ctx, http.MethodPost, "/api/v2/templatebuilder/sessions", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusNoContent {
|
||||
return ReadBodyAsError(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TemplateBuilderCreateTemplate composes a template from a base and modules,
|
||||
// validates it via a provisioner import job, and creates the template.
|
||||
func (c *Client) TemplateBuilderCreateTemplate(ctx context.Context, req TemplateBuilderCreateTemplateRequest) (TemplateBuilderCreateTemplateResponse, error) {
|
||||
|
||||
Reference in New Issue
Block a user