Skip to content

Formbase 0.14.0

Choose a tag to compare

@github-actions github-actions released this 29 Sep 06:27
· 36 commits to main since this release

Pairs with MorphDB 0.14.x. A correction is a new append — and now it can say which record it corrects. Breaking for code that
implements IRawStore or reads StoredDocument.Body, and for callers that passed a cancellation token
positionally to AcceptAsync/AppendAsync.

Added

  • Record identity. A document may name the record it belongs to with a RecordKey (an opaque
    string, compared exactly, scoped to the form type): FormbaseEngine.AcceptAsync(type, body, recordKey: key), over HTTP POST /formtypes/{type}/documents?recordKey=…. The projection shows each
    record once — the document with the latest watermark — so a corrected record is one row and a count
    counts it once. Documents without a key are records of their own, exactly as before.
  • Retiring a record. FormbaseEngine.RetireAsync(type, key), over HTTP
    DELETE /formtypes/{type}/records?recordKey=…, appends a retirement: a document with no body that
    takes the record out of the projection while its earlier documents stay in the raw stream. Appending
    under the key again brings the record back. Idempotency keys work for retirements as for intake.
  • Record keys are compared exactly and are not Unicode-normalized: the same text in two normalization
    forms (a file name from one system in NFC, from another in NFD) names two records. Normalize keys that
    come from file names or user input before sending them.
  • RecordFold.Latest — the fold the projector applies (latest per key, retired keys dropped), public so
    any reader of the raw stream can get records rather than appends.
  • Raw reads carry recordKey and retired (StoredDocument.Key, StoredDocument.IsRetirement). A
    retirement reads back with a null body; read retired rather than testing the body, since a document
    whose content is the JSON value null has a null body too.
  • Projected tables gain the fb_record_key bookkeeping column. Record reads still return the declared
    fields only.

Changed

  • A durable host refuses to start — exit code 78, naming the setting — when MorphDB answers that the
    configured Formbase:MorphDb:ProjectId does not exist. It used to report ready and fail its first
    projection with an unhandled error. A MorphDB that cannot be reached at startup does not stop the
    start, as before.
  • Breaking: IRawStore.AppendAsync(type, id, body, key = null, cancellationToken) takes the record key
    before the cancellation token, and IRawStore.RetireAsync is new — a raw-store adapter implements both.
    IIntakeService.AcceptAsync likewise takes recordKey before the token, and IIntakeService.RetireAsync
    is new. A call that passed the token positionally names it (cancellationToken: ct).
  • Breaking: StoredDocument.Body is nullable — null for a retirement. StoredDocumentResponse.Body
    is nullable over HTTP for the same reason, next to the new recordKey and retired.
  • An idempotency key reused under another record key, or for a retirement where it named a document (or
    the reverse), is refused like a key reused with a different body.
  • Schema intelligence skips retirements when sampling a form type's documents.
  • SQLite and PostgreSQL files and schemas from 0.13.x are upgraded in place on first use: the raw
    table gains its record-key column (and, on SQLite, a retirement column), and every document it holds
    reads back as a record of its own. On PostgreSQL the check runs before any change, so an up-to-date
    schema takes no table lock at startup. An existing projected table gains fb_record_key the next time
    it is rebuilt; until then nothing in it is keyed, because nothing appended before the upgrade was.
    The upgrade is one-way. Once a record has been retired, 0.13.x can no longer read the form type's
    raw stream (it answers 500), and a projection it runs leaves the table empty. Back up the database
    before upgrading if you may need to go back.
  • A bound field's target.keyField (EntityRef.KeyField) is documented for what it is: the field on
    the target form type whose value the bound field carries, not a key for finding the target record.
    The API reference had shown it one way and nothing said which; nothing about its behaviour changes.

Dependencies

  • Microsoft.Extensions.AI.Abstractions and Microsoft.Extensions.AI.OpenAI 10.10.1 (was 10.10.0), a patch.
  • Formbase.MorphDb depends on MorphDB.Client 0.14.0 (was 0.13.1), and the live tests and the compose file
    run the ghcr.io/iyulab/morphdb:0.14.0 image.