MishkaCmsCore.Activity.ActivityEntry (Mishka CMS Core v0.0.1)

Copy Markdown View Source

Synthetic, read-only resource representing one unified activity-log entry, and the gervaz timeline table over it.

No database table — Ash.DataLayer.Simple plus a single manual read whose manual_read/2 delegates to MishkaCmsCore.Activity.Query, which merges every AshPaperTrail version table + AshEvents event log + Oban jobs in ONE DB-side UNION. Attributes mirror the MishkaCmsCore.Activity.Behaviours.Source entry shape so gervaz columns/filters bind by name and the stream gets a stable :id.

Tenant scoping is enforced in the query (Ash won't enforce multitenancy for a manual-over-Simple read): a master sees every site; a tenant sees its own site plus global rows of the shared runtime resources (Query.shares_globals?/1), never another tenant. The :activity_site column is shown to everyone (a tenant's rows resolve to its own site or "Global").

Summary

Functions

The log's KPI totals for viewer — %{total:, info:, warn:, error:} over the sites and the areas it may see (MishkaCmsCore.Activity.Query.counts/1). All zero for no viewer.

Builds an %ActivityEntry{} struct from a unified source entry map, deriving :kind (content vs background job). Used by the manual read and the live path.

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.

Whether a live record should appear in a given table view — tenant-visible AND matching the active filters. Used by the realtime on_realtime hook so a live insert never violates the current Severity/Source filter.

How many entries the table's current view matches — the total behind "20 of 10,000+ shown", scoped to the viewer and narrowed by whatever filters are active. Called from the on_load hook, which is where gervaz's filter state lives.

Types

t()

@type t() :: %MishkaCmsCore.Activity.ActivityEntry{
  __lateral_join_source__: term(),
  __meta__: term(),
  __metadata__: term(),
  __order__: term(),
  action: term(),
  actor_label: term(),
  aggregates: term(),
  badge_label: term(),
  badge_tone: term(),
  calculations: term(),
  id: term(),
  inserted_at: term(),
  kind: term(),
  message: term(),
  resource_label: term(),
  severity: term(),
  site_id: term(),
  site_label: term(),
  source_id: term(),
  tone: term(),
  user_id: term()
}

Functions

counts(viewer)

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

The log's KPI totals for viewer — %{total:, info:, warn:, error:} over the sites and the areas it may see (MishkaCmsCore.Activity.Query.counts/1). All zero for no viewer.

default_short_name()

from_entry(entry)

Builds an %ActivityEntry{} struct from a unified source entry map, deriving :kind (content vs background job). Used by the manual read and the live path.

The day heading the timeline draws is the read's, not the entry's, so mark_days/2's marks ride along as metadata (see MishkaCmsCoreWeb.Templates.ActivityTimeline); a live entry carries none and simply gets no heading.

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.

live_visible?(record, state)

Whether a live record should appear in a given table view — tenant-visible AND matching the active filters. Used by the realtime on_realtime hook so a live insert never violates the current Severity/Source filter.

primary_key_matches?(left, right)

total(state)

@spec total(map()) :: non_neg_integer()

How many entries the table's current view matches — the total behind "20 of 10,000+ shown", scoped to the viewer and narrowed by whatever filters are active. Called from the on_load hook, which is where gervaz's filter state lives.