GoodAnalytics.Core.Visitors (GoodAnalytics v0.1.1)

Copy Markdown View Source

Context for visitor CRUD and lifecycle operations.

Summary

Functions

Gets the attribution path for a visitor.

Removes all PII, events, and identity signals for a visitor (GDPR forget).

Gets a visitor by host-app external ID within a workspace.

Gets a visitor by ID.

Gets a visitor by ID, scoped to a workspace.

Lists recent visitors for a workspace.

Populates visitor.device for visitor_id if and only if the current value is empty (first-event-wins), mirroring maybe_set_geo/2. Device parsing is synchronous, so this is called inline at ingest rather than via async enqueue.

Populates visitor.geo for visitor_id if and only if the current value is empty. Used by async ingest enrichment (beacon, REST API) where we want first-event-wins semantics without re-running identity resolution.

Gets the event timeline for a visitor, ordered by inserted_at desc.

Updates visitor attribution fields (extension point for GoodPartners).

Updates the lifecycle status of a visitor.

Functions

attribution(visitor_id)

Gets the attribution path for a visitor.

forget(visitor_id)

Removes all PII, events, and identity signals for a visitor (GDPR forget).

Clears all identifying fields and deletes associated events.

get_by_external_id(workspace_id, person_external_id)

Gets a visitor by host-app external ID within a workspace.

get_visitor(id)

Gets a visitor by ID.

get_visitor(workspace_id, id)

Gets a visitor by ID, scoped to a workspace.

list_visitors(workspace_id, opts \\ [])

Lists recent visitors for a workspace.

maybe_set_device(visitor_id, device)

@spec maybe_set_device(Ecto.UUID.t(), map()) ::
  {:ok, 0 | 1} | :noop | {:error, :not_found}

Populates visitor.device for visitor_id if and only if the current value is empty (first-event-wins), mirroring maybe_set_geo/2. Device parsing is synchronous, so this is called inline at ingest rather than via async enqueue.

Returns {:ok, count} (1 set, 0 already populated), :noop for empty input, or {:error, :not_found} when the visitor does not exist.

maybe_set_geo(visitor_id, geo)

@spec maybe_set_geo(Ecto.UUID.t(), map()) ::
  {:ok, 0 | 1} | :noop | {:error, :not_found}

Populates visitor.geo for visitor_id if and only if the current value is empty. Used by async ingest enrichment (beacon, REST API) where we want first-event-wins semantics without re-running identity resolution.

Implemented as a conditional UPDATE (SET geo = ? WHERE id = ? AND (geo = '{}'::jsonb OR geo IS NULL)) so the first-event-wins invariant holds under concurrent click + beacon writes for the same visitor.

Returns {:ok, count} where count is 1 when the row was updated and 0 when another writer beat us to it, :noop when called with empty input, or {:error, :not_found} when the visitor does not exist.

timeline(visitor_id)

Gets the event timeline for a visitor, ordered by inserted_at desc.

update_attribution(visitor_id, attrs)

Updates visitor attribution fields (extension point for GoodPartners).

update_status(visitor_id, status)

Updates the lifecycle status of a visitor.