MishkaDocument.WorkspaceVersionLineage (Mishka Document v0.0.1)

Copy Markdown View Source

Tracks the lineage and cloning history of workspace versions.

This resource maintains a detailed audit trail of version cloning operations, recording when a workspace version was created by cloning another version. It provides a clear parent-child relationship and timestamp for version branching.

This is useful for:

  • Building version history trees
  • Tracking when versions were cloned
  • Auditing version branching operations
  • Understanding version evolution over time

Multi-tenant isolation ensures lineage records are properly segregated between sites.

The cloned_at_not_future check reads cloned_at <= timezone('UTC', clock_timestamp()), and both halves of that matter:

  • clock_timestamp() rather than NOW(), because NOW() is the TRANSACTION's start time. Any caller that opens a transaction, does some work and only then writes a lineage row with a freshly-read timestamp was rejected for a clock it never got ahead of — the longer the transaction, the likelier the false rejection.
  • timezone('UTC', ...) because cloned_at is a timestamp WITHOUT TIME ZONE holding UTC, while clock_timestamp() is a timestamptz. Comparing the two directly makes Postgres read the column in the SESSION's zone, so on a connection west of UTC a genuinely past value is rejected (measured: a value two hours old fails under America/New_York). Wrapping pins both sides to UTC, which is what the column actually stores. MishkaBlog.BlogPost's scheduled_publish_at check already does this.

The comparison is strictly more permissive than the old NOW() one, so no row that satisfied it can fail the new one.

Summary

Functions

Apply the full GuardedStruct pipeline (sanitize → validate → derive → main_validator) to attrs and return either {:ok, transformed_attrs} or {:error, errors}. Wire this into an Ash.Resource.Change to plug guardedstruct rules into Ash's changeset pipeline.

O(1) lookup of a field's compile-time metadata by name. Returns nil for unknown fields.

Compile-time-baked MapSet of every field name owned by the guardedstruct block. Used by GuardedStruct.AshResource.Change to decide, in O(1), whether a key in changeset.atomics belongs to the pipeline.

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.

Types

t()

@type t() :: %MishkaDocument.WorkspaceVersionLineage{
  __lateral_join_source__: term(),
  __meta__: term(),
  __metadata__: term(),
  __order__: term(),
  aggregates: term(),
  calculations: term(),
  cloned_at: term(),
  id: term(),
  inserted_at: term(),
  new_version: term(),
  new_version_id: term(),
  site: term(),
  site_id: term(),
  source_version: term(),
  source_version_id: term(),
  updated_at: term()
}

Functions

__guarded_change__(attrs, error? \\ false)

Apply the full GuardedStruct pipeline (sanitize → validate → derive → main_validator) to attrs and return either {:ok, transformed_attrs} or {:error, errors}. Wire this into an Ash.Resource.Change to plug guardedstruct rules into Ash's changeset pipeline.

The function is named __guarded_change__ because it does more than validate — it can also transform values (trim, downcase, slugify, auto-fill, etc.) before they reach the data layer.

__guarded_field_meta__(name)

O(1) lookup of a field's compile-time metadata by name. Returns nil for unknown fields.

__guarded_field_name_set__()

Compile-time-baked MapSet of every field name owned by the guardedstruct block. Used by GuardedStruct.AshResource.Change to decide, in O(1), whether a key in changeset.atomics belongs to the pipeline.

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.

json_api_match_route(_, _)

primary_key_matches?(left, right)