Skip to content

Store entries with versions checked against their content type #2486

Closed

Description

@pournasserian

Part of #2416 (F-CNT). First slice: entries of every content type are stored with a full version history (CNT-02, ADR-0005). Each write is checked against the entry's content type definition and the field types it uses, and makes a new version that is never overwritten. An entry keeps two pointers, to its latest draft and to its published version. Entity types are content types too (#2465), so an entity such as a product is stored the same way, with its facts.

Scope

  • Core (FluentCMS.Core.Schema): IFieldType gains ValidateValue(value, settings, fieldTypes), which checks one value of a field and returns errors with value_invalid instead of throwing. Every built-in implements it, following what DescribeValue already tells agents: text and rich text with maxLength, numbers with integer, min and max, booleans, dates and times (date-only too), enum values, media and reference ids as UUIDs, lists item by item through the registry, and any JSON. FieldTypeConformanceTests gains values a type must accept and reject, checks that the type accepts its own documented example, and that it never throws whatever a value holds.
  • Core (FluentCMS.Core.Content): the entry model. An Entry has its id, site, content type, latest-draft version and published version, plus the latest draft's fields. An EntryVersion holds its number, the content type version it was checked against, its fields as one JSON document and when it was written.
  • Core: an entry document is a JSON object with one property per field. All problems come back at once, each naming its field (UX-03): a property that is not a field of the type, a required field that is missing, and a value its field type rejects. An explicit null reads as an absent value. On an entity type every value is a fact, {value, source, owner, validFrom, validUntil, lastVerified}, checked against the shape the schema document describes (SCH-03, SCH-06).
  • Core: IEntryDirectory, returning Result for expected failures. It lists the entries of a content type, reads an entry, reads any of its versions, lists its history, creates an entry, and updates an entry from the version the caller read. Writes are checked against the content type's current version.
  • Infrastructure: Entries and EntryVersions tables, scoped to the site. Fields are jsonb on PostgreSQL. A write inserts a version row and moves the latest-draft pointer from the version the caller named, so a concurrent update gets a conflict instead of overwriting. The published pointer is stored but moved only by publish, which is not part of this slice. A trigger on both providers refuses any UPDATE of a version row.
  • Audit (AUD-01, AUD-N1): entry.created and entry.updated, with the entry as subject and, for an update, the base version's row as previous version, appended in the same unit of work.
  • Migrations for SQLite and PostgreSQL, each with a working Down.

Out of scope

Acceptance

  • A created entry belongs to the site, starts at version 1 with no published version, reads back with its fields, and entry.created names it.
  • Every update stores the next version. Earlier versions stay readable, the history lists them all in order, and entry.updated carries the base version's row as previous version.
  • An update from a version other than the latest gets version_conflict and writes nothing. Of two concurrent updates from one version, one wins and the other gets a conflict.
  • An unknown property, a missing required field and an invalid value are reported together, each naming its field, and nothing is stored. An unknown content type, entry or version fails with its own code.
  • An entity's facts are checked for their value and every metadata property.
  • An UPDATE of a version row fails on both providers.
  • A create in an abandoned unit of work leaves neither the entry nor its event, and a failed write leaves the caller's unit of work able to commit.
  • Every built-in field type passes the extended conformance tests, and so do the sample plugin types.
  • Everything above passes on SQLite and PostgreSQL, the new migration's Down works on both, and ProjectReferenceRulesTests pass with no new package.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type:featureNew capabilityv2FluentCMS v2 (v2 branch)

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions