Skip to content

Catalog: two audit payloads store enums as ordinals, so reordering an enum silently rewrites history #746

Description

@mforce

Found while designing #722; recorded as unowned in that slice's ownership map and filed late — caught by the owner after #722 closed.

What

AuditWriter serialises details with new JsonSerializerOptions(JsonSerializerDefaults.Web) (AuditWriter.cs:17-18), which registers no JsonStringEnumConverter. A bare enum in a payload is therefore stored as its underlying integer.

Two call sites do exactly that:

  • src/Cluckwork.Application/Features/Catalog/CreateProduct/CreateProductHandler.cs:65 — details: new { product.Name, product.ProductType, EggGrade = grade.Name }, storing ProductType as a number
  • src/Cluckwork.Application/Features/Catalog/UpdateProduct/UpdateProductHandler.cs:78 — same shape, storing DefaultUnit as a number

Why it matters

A stored ordinal is only meaningful against the enum's member order at the time it was written. Reorder or insert a member and every historical row silently re-reads as a different value — with nothing failing, because the JSON is still valid and the number is still a number. That is a data-retention defect in the one table whose whole purpose is being trustworthy after the fact.

The repo already does the right thing elsewhere

Serialising the NAME is the dominant convention for this payload shape:

  • UpdateFarmSettingsHandler.cs:158,159,163,164 — .ToString() on four enums inside details:
  • UpdateEggUnitConversionHandler.cs:30 — same

#722 followed that convention for ListPriceBasis and pinned it with two mutation rows (dropping .ToString() reddens a named test on each call site, because GetString() throws on a JSON number).

Scope

Add .ToString() at the two call sites above and assert the name in each handler's audit test, with a mutation row per site proving the assertion reddens. Do not add a converter to AuditWriter's options — that would silently change the shape of every existing payload across ~25 call sites.

Note on existing rows

Rows already written carry ordinals and cannot be repaired from the data alone. This issue prevents the defect widening; a backfill would need the enum's member order at write time, which is not recorded.

Activity

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

    area:apiAPI/endpoint layersliceThin vertical work item

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions