MishkaDocument.EventLog (Mishka Document v0.0.1)

Copy Markdown View Source

Minimal event log for tracking activities in MishkaDocument.

Stores only essential information:

  • What happened (resource, action)
  • When it happened (timestamp)
  • Who did it (actor)
  • Which record (record_id)
  • Minimal context (site_id, title/name for display)

Does NOT store full input data or changed attributes to keep storage minimal.

The gervaz table over it is the document event log at /admin/dashboard/document/event-logs, drawn by the shared MishkaCmsCoreWeb.Templates.ActivityTimeline — the same timeline the unified log at /admin/dashboard/runtime/activity uses, over the very same rows. Everything the timeline reads off a record comes from here: :severity, the content-change :tone it tints a create/update/ destroy row by, the day marks (MishkaDocument.Resources.Helpers.DayMarks) and :occurred_at.

Tenant scoping is MishkaDocument.Resources.Helpers.TenantSiteFilter's, on the :table_read list and :table_get single-row reads both roles use — site_id lives in JSONB, so Ash multitenancy cannot apply.

Every read needs one of the two document areas, admin:<dashboard|api>:document:workspace or admin:<dashboard|api>:document:section, and returns only the events of the records that area holds: a workspace admin reads workspace and version events, a section admin the rest (logged/1). A master (a user whose site_id is nil) reads every site; a site admin only rows carrying its own site, whatever read or site_id argument it asks with. Writes belong to the system and to masters. Calls made with authorize?: false are not checked.

Only :table_read carries the page-level day marks (MishkaDocument.Resources.Helpers.DayMarks); :table_get deliberately omits them, since one isolated realtime row has no page to mark.

Summary

Functions

Who did event, for the reader whose read loaded its :actor and :actor_master?.

How many events the viewer can see, as %{total: n, info: n, warn: n} — the log's KPI row.

Validates that the keys in the provided input are valid for at least one action on the resource.

Same as input/1, except restricts the keys to values accepted by the action provided.

The resources whose events an area reads: :workspace for document:workspace, :section for document:section. Every resource writing to the log is listed under exactly one area.

Types

t()

@type t() :: %MishkaDocument.EventLog{
  __lateral_join_source__: term(),
  __meta__: term(),
  __metadata__: term(),
  __order__: term(),
  action: term(),
  action_type: term(),
  actor: term(),
  actor_id: term(),
  actor_master?: term(),
  aggregates: term(),
  calculations: term(),
  changed_attributes: term(),
  data: term(),
  id: term(),
  metadata: term(),
  occurred_at: term(),
  record_id: term(),
  resource: term(),
  severity: term(),
  tone: term(),
  version: term()
}

Functions

actor_label(event)

@spec actor_label(map()) :: String.t()

Who did event, for the reader whose read loaded its :actor and :actor_master?.

The email the reader may see, "Master" for a master, "System" for an event no user did (a replay, a seed, a job) and "Unknown" for an actor the reader's read did not return.

counts(actor)

@spec counts(map() | nil) :: %{
  total: non_neg_integer(),
  info: non_neg_integer(),
  warn: non_neg_integer()
}

How many events the viewer can see, as %{total: n, info: n, warn: n} — the log's KPI row.

Counted through :table_read, the very action the table reads, so a figure can never disagree with the list beside it: a master counts every site, a tenant only its own. There is no :error figure — an event log records what a resource DID, never a failure, so MishkaCmsCore.Activity.Helpers.severity_for/1 only ever reads one of these as :info or :warn.

default_short_name()

input(opts)

@spec input(values :: map() | Keyword.t()) :: map() | no_return()

Validates that the keys in the provided input are valid for at least one action on the resource.

Raises a KeyError error at compile time if not. This exists because generally a struct should only ever be created by Ash as a result of a successful action. You should not be creating records manually in code, e.g %MyResource{value: 1, value: 2}. Generally that is fine, but often with embedded resources it is nice to be able to validate the keys that are being provided, e.g

Resource
|> Ash.Changeset.for_create(:create, %{embedded: EmbeddedResource.input(foo: 1, bar: 2)})
|> Ash.create()

input(opts, action)

@spec input(values :: map() | Keyword.t(), action :: atom()) :: map() | no_return()

Same as input/1, except restricts the keys to values accepted by the action provided.

logged(atom)

@spec logged(:workspace | :section) :: [module()]

The resources whose events an area reads: :workspace for document:workspace, :section for document:section. Every resource writing to the log is listed under exactly one area.

MishkaDocument.EventLog.logged(:workspace)
#=> [MishkaDocument.Workspace, MishkaDocument.WorkspaceVersion]

primary_key_matches?(left, right)