feat/descriptor #35

Merged
christian.keller merged 35 commits from feat/descriptor into main 2026-08-18 10:09:47 +00:00

Add descriptors as a first class citizen of CRUDular. With them you can add descriptive properties to be exposed togeher with your business key properties.

Add descriptors as a first class citizen of CRUDular. With them you can add descriptive properties to be exposed togeher with your business key properties.
Adds the DB-independent foundation for entity descriptors:

- api: `Descriptor<E, K>` marker interface; `@CrudDescriptor` and `@CrudBackRef`
  (with `ListMode`) annotations; `MetaDescriptor` meta type.
- api: `MetaField` exposes `isDescriptor`, `elementType`, and the `@CrudBackRef`
  opt-in; `MetaEntity.nestedFields` is scoped to `Descriptor` (forward embed) and a
  new `descriptorListFields` category holds back-reference collections;
  `MetaType.descriptor` is resolved per entity.
- base: `CrudMetaModel` discovers `Descriptor` implementers by their type argument,
  wires `MetaType.descriptor` and the referencedType of forward/back-ref fields;
  `DescriptorModelVerification` enforces one-per-entity, key fields mirroring the bk
  (name+type), flat nullable/defaulted descriptive fields, and `@CrudBackRef` list
  placement. Registered in the base auto-configuration.

Descriptor key fields mirror the entity's business key as-is (carrying references),
per docs/plans/descriptors.md and ADR 0003.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reads a descriptor from its jOOQ UDT value and reconstructs the nested business key,
validated against the reshaped AuthorView / book_descriptor scaffolding:

- test: BookDescriptor is now a formal Descriptor<Book, BookKey> (key mirrors BookKey:
  author reference + title, plus the descriptive genre); AuthorView.books opts in via
  @CrudBackRef. New migration redefines the book_descriptor UDT + author_view (adds genre,
  column order [key leaves…, descriptive…]). jOOQ test schema regenerated (the checked-in
  sources were stale vs jOOQ 3.21.6; wholesale regen + IDE reformat per CLAUDE.md).
- repo-jooq: DescriptorDecoder rebuilds a business key from the UDT's flat leaf scalars
  (recursing into references, as Descriptor.key does) and fills descriptive fields — no DB
  access. SelectTable reads a back-reference array as an in-place value column; SelectField
  routes descriptor/back-ref columns through the decoder. The built-in decode replaces the
  ITs' hand-written RepoValueMapper.
- api: MetaField.isReferenceJoin distinguishes a joinable reference (BK / forward Descriptor)
  from an embedded back-reference array; search (SearchConditions, Selection.referencePrefix,
  JooqConfigurer, JooqSearchVerification) uses it so a back-ref array still unnests rather than
  joins. JooqModelVerification skips descriptor columns (their UDT is verified in a later phase).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JooqModelVerification now checks each entity's descriptor against the generated schema: the
hand-written composite type (@CrudDescriptor.type, default dt_<repoTable>) must exist and its
attributes must match the descriptor's flat column shape — the business key's leaf scalars (in
MetaKey.flattenedFields order) followed by the descriptive fields — matched positionally by the
same lenient storage-type compatibility the read path relies on.

Tests: GadgetDescriptor added to the verification model; DDL-synthesis drift tests cover a missing
UDT, wrong attribute count, and an incompatible attribute type (jOOQ's DDL interpreter parses
CREATE TYPE … AS (…)). The real-schema happy path confirms book_descriptor matches BookDescriptor.

Deferred: backing-view + search-column verification (§2.2 bullets 2-3) applies to forward/derived
descriptors read via a view, for which the concrete model has no scenario yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DescriptorModelVerification now checks each @CrudBackRef collection declares a write mode
(UNSET → finding, so behaviour is unambiguous) and, for the writable modes, that `via` names a
real reference on the referencing entity with the shape the mode requires: SET_NULL needs a
nullable child reference; RELATION needs a pure two-key link entity (else nudge to
RELATION_DESTRUCTIVE). READ_ONLY and the valid SET_NULL/RELATION shapes pass cleanly.

Fixtures (descfix.backref) cover each failing mode alongside its passing counterpart.

The back-reference read path (arrayColumn aggregation) already landed with the jOOQ UDT read
path (§3). Deferred: via-aggregation read without a pre-aggregated arrayColumn, and the ?using
back-link suggestion/rendering (REST, with the write side).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RestEntityParser.parseValue now treats a forward Descriptor field like a BK: a textual URI is
resolved to the business key (RestEntityParser.parseKey → uriService.uriToKey), whose flat fields
bind into a key-only descriptor — descriptive fields absent — with no DB access. A bare key object
or a full descriptor object are passed through and bind the same way.

Test (isolated descfixrest fixture with a forward Descriptor field) covers the URI and the bare
key-object inputs.

Deferred (the remaining §5 write side): forward-descriptor read via a backing view
(SelectMode-to-view), and the repo-side list-write execution for SET_NULL / RELATION /
RELATION_DESTRUCTIVE with its write ITs — these need the forward-read DB machinery and writable
descriptor scenarios not present in the current model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Unify the "referenced but not joinable" distinction on MetaField.isReferenceJoin in
  SelectTable.init (was spelled two ways), and drop the unreachable forward-descriptor branch from
  SelectField.fromDb (forward descriptors are join-routed; that decode belongs to the deferred
  forward-read work).
- Collapse the identical Array/Collection branches of decodeDescriptorList; remove two redundant casts.
- Tighten the descriptive-field flat-scalar check to also reject a collection of references
  (List<BK> / List<Descriptor>), not only a direct reference field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A user how-to under docs/howto/user covering the link < descriptor < expand choice, declaring a
Descriptor + its UDT, embedding a List<Descriptor> back-reference via a view, and the @CrudBackRef
write-mode decision table (with cautions on RELATION / RELATION_DESTRUCTIVE and an honest note on
current execution support). Cross-links the older hand-wired "complex object" how-to to it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore: cleanup
All checks were successful
/ release (pull_request) Successful in 2m33s
0e1befe67b
chore: cleanup
All checks were successful
/ release (pull_request) Successful in 2m32s
6d7a544de2
matthias.weisse requested changes 2026-08-04 08:53:30 +00:00
Dismissed
@ -0,0 +1,28 @@
package descfix.nonopt

Package name looks strange

Package name looks strange
christian.keller marked this conversation as resolved
chore: cleanup
All checks were successful
/ release (pull_request) Successful in 2m36s
9b0255f1e4
An entity field typed as a Descriptor rather than a BK could not be read: the
jOOQ planner joined its table KEY-only, so the descriptive columns never came
back and the owning entity could not be constructed. Add the missing middle
rung between KEY and FULL.

The descriptor is read from FLAT columns off one row — the entity's business-key
columns plus the descriptor's descriptive ones — never a UDT. That is KEY mode
plus a few value columns: no ROW(...)::udt raw SQL, no generated-UDT binding,
dialect-neutral. The UDT keeps its existing role as the storage shape a
back-reference collection aggregates into.

- JooqDescriptorData resolves the source once per type at configure time: the
  descriptor's backing view when the schema has one, else the entity's own
  table. Resolution is non-throwing — an unresolvable column degrades the field
  to today's key-only read and is reported by verification instead.
- An own-row descriptor upgrades the reference's own join in place, so no extra
  join is emitted and the dotted-path alias space searches and ordering address
  is untouched. A view-backed source, or an expanded reference, gets a companion
  `<path>#d` join beside it.
- RowDecoder materializes the descriptor (key rebuilt from the key columns
  alone, so a null descriptive value cannot null the key) and threads it to the
  parent through nestedFields.
- Writing a descriptor degrades to its key, in both insert and update.
- Verification gains the read-source checks: the descriptive/business-key
  columns exist on the source, a view exposes the id join column, an explicitly
  declared view exists. A forward descriptor's `<name>_id` column is now checked
  as the real foreign key it is.
- MetaFieldUtils.crudColumn suffixes `_id` for Descriptor fields, so retyping a
  reference from its key to its descriptor needs no migration.

ADR-0003 is amended: a derived descriptor's view exposes flat columns and an id
join column for the forward read, not a `row` column of the UDT.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(descriptors): composite column reads, cell mappers, and the missing jOOQ guardrails
All checks were successful
/ release (pull_request) Successful in 2m33s
e23ac91c0a
Three related strands, sharing the verification, fixtures and docs they touch.

**`@CrudDescriptor.column` becomes functional.** It was carried to MetaDescriptor and
never read. Its default changes from `row` to blank, which states the flat read the
forward path has used since ADR-0003's first amendment; naming a column selects that
single column of the read source (backing view, else the entity's own table) and maps
the value it holds to the descriptor class — the UDT-valued shape the ADR originally
described, now opt-in (amendment 2). Such a table selects that column alone and joins
no nested key tables: the business key is rebuilt from the value's leading cells.

**Value mappers reach the composite decode paths.** `JooqDescriptorCell` pairs each
UDT cell with the model field it fills and that field's read mappers, resolved once per
described entity from the UDT's attribute types. `decodeDescriptor` converts cells
before rebuilding the key, so an enum key leaf works, not just a descriptive field —
and a back-reference array decodes through the same cells. No UDT in the schema means
no cells and unconverted pass-through, as before.

**Verification gains what §2.2/§2.3 asked for and the jOOQ side lacked.** A
back-reference collection was skipped entirely; it is now checked for an array column
of the referenced descriptor's UDT, an `arrayColumn` naming the column actually read, a
referenced entity that declares a descriptor at all, the UDT attributes a search
unnesting the array addresses by name, and — for `SET_NULL` — a child foreign key the
database permits to be nulled. A composite descriptor is checked for its column and
that column's type instead of the flat columns it never selects, and a view-backed
source's business-key foreign keys are now type-checked against the referenced id.

Shared test schema: `dt_badge` / `badge` / `d_badge` / `badge_holder` back a composite
descriptor whose enum cells force the mappers, driving JooqRepoCompositeDescriptorIT
end-to-end; the jOOQ sources are regenerated accordingly.

Also fixes a latent assumption in CrudOpenApiServiceTest: Swagger sorts a schema's
`required` list, so comparing it to the property order only ever held for single-field
keys — the first multi-field key schema in the sample model broke it.
Descriptors no longer pay for a composite type they never use, view entities are
accepted as storage-equivalent to their base, and the jOOQ guardrail starts closing
the gaps found while auditing what it actually checks.

Descriptor UDTs (ADR-0003 revised in place)
- A composite type is required only where the descriptor is exchanged as a single
  value: `@CrudDescriptor(column = …)`, or a `@CrudBackRef` aggregating it. The two
  flat forward shapes read plain columns and need none. A type that exists is still
  shape-verified, so drift is caught before the first aggregation.
- `MetaDescriptor.aggregatedBy` records which collections aggregate a descriptor,
  wired in `CrudMetaModel`; the jOOQ side derives the requirement from it.

viewOf equivalence in storage
- A stored composite type is accepted when its name is on the referenced entity's
  view chain *and* its attributes match the decoded descriptor positionally --
  sharing a base pins only the key leaves, never the descriptive tail.
- A declared foreign key may name any table on the chain; the referenced id falls
  back along it, since a view need not expose one.
- `viewFindings` accepts VIEW and MATERIALIZED_VIEW, and anchors the base on the
  chain's root table, so a chained viewOf passes.

S0 -- verification no longer runs too late
- `JooqBinding.dbMeta` resolves on first use. Repositories are configured before the
  model verification runs, so resolving eagerly made the first drift throw out of
  `configureRepos`, suppressing every finding -- including the one naming that drift --
  and bypassing `crudular.model.verification`.

S1 -- silently wrong results
- Entities sharing a `repoTable` are checked for a discriminator on each, one shared
  column, and distinct values; any of the three makes one type's reads return
  another type's rows.
- `arrayCondition` unnests the array on its owning table's alias. It resolved every
  array on the subject, so `author.keywords` unnested whatever subject column shared
  the reference field's name.

Docs: `CrudDescriptor` KDoc rewritten around the four shapes and the positional
contract, `CrudBackRef.arrayColumn` corrected (no `via` read fallback),
`CrudEntity.viewOf` documents chaining and matview staleness, plus a new
"persistence mapping and verification" how-to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Model and column nullability disagreeing breaks at runtime in two different
places, neither of them at startup:

- a non-null property over a nullable column fails at read, when a NULL reaches
  the entity constructor;
- a nullable property over a NOT NULL column with no default fails at insert,
  because InsertBuilder skips null values and leaves the column to its default.

Only real tables carry usable information -- a view's columns are nullable in the
catalog whatever the query guarantees -- and id/version/discriminator are exempt
from the write direction, since the repo supplies them itself.

The check immediately found real drift in the shared test schema: `email.email`
was nullable while `Email.email` is non-null *and* is Email's business key, so a
null there would have had no identity at all. Fixed by migration and the jOOQ
sources regenerated (only Email/EmailRecord kept; the rest of the regeneration was
pure re-indentation and is restored). Fixture DDL gained the NOT NULL it always
implied -- `unique` does not imply it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
§2.2 -- business key and id uniqueness
Identity runs through a scalar sub-select (`SELECT id FROM t WHERE <bk>`), so a
business key without a unique constraint fails with "more than one row returned by
a subquery" on the first duplicate -- at request time, on data. Both the key and
the id are now checked against the table's unique keys. A key over a *subset* of
the key columns is accepted (it constrains more), and under single-table
inheritance the discriminator counts towards the key, since the sub-select filters
on it too -- `animal` is legitimately unique per (kind, name), not per name.
Views are exempt: they carry no constraints and their identity is the base's.

§6.2 -- order-by no longer names an alias nothing joins
`Ordering.orderField` navigated any `referencedType`, while the join planner stops
at non-joinable references. A path through a back-reference collection therefore
produced `"books"."title"` -- an alias no LEFT JOIN introduces, i.e. invalid SQL.
It now applies the planner's own `isReferenceJoin` rule.

§6.1 -- a path that resolves in the model but not to a column
Silently tolerated, then threw `No searchable column for path` the first time the
search ran. The reachable case is a reference into another repository's entity:
the path navigates fine, but its table belongs to another schema and cannot be
joined. Reported now, with the repo named. Embedded record arrays stay exempt --
being unresolved is their normal state, since they are unnested rather than joined.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
§5.2 -- attribute names of any record array, not just back-references
A search into an array is unnested and addresses the element by attribute name,
so the SQL depends on the composite type's attribute names. That is true of any
embedded record array; the model-level check only sees the Kotlin element type.
The check now resolves the component type off the column and applies to all of
them.

§4.1 -- both conversion directions are required
typeCompatible passed if a read OR a write mapper existed. CommonMappers ships
LocalDate -> Instant with no inverse, so an Instant field over a date column
verified clean, read fine, and failed on insert with the unconverted value handed
to JDBC. Read and write are now checked separately and the message names the
missing direction. Descriptor sources are checked read-only, which is what they
are.

§4.2/§4.3 -- collections are compared element-wise
Any Collection was accepted against any array, so List<String> over integer[]
verified clean. A collection must now map to an array column at all, and its
element type must convert in both directions in its own right.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
§4.7/§4.5 -- the two columns with no model field behind them
The discriminator is bound as Field<String> and compared to a fixed text value, so
a non-text (or too-short) column either fails to bind or never matches. The version
column is set to 1 on insert and incremented on update, which a non-numeric column
cannot take. Both were checked for existence only.

§2.3 -- the id sequence and the id column
Every insert draws its id from nextval(crud_id_seq) and reads it back as a number,
so a missing sequence fails on the first POST. The sequence name is now threaded
from CrudJooqProperties and checked once per repo, together with the id column's
type and a GENERATED ALWAYS identity that would reject the supplied value. Type
checks skip unknown (`Object`) column types, which is what a DDL-parsed view yields.

§2.4 -- writes against a relation that cannot take them
An entity enabling create/update/delete over a view is reported: Postgres rewrites
only a narrow auto-updatable shape, anything else needs an INSTEAD OF trigger.

This found a real defect in the shared test model: AuthorView took the default
@CrudEntity operations -- all ON -- over the `author_view` database view, with no
trigger behind it. It now declares read+search, and the view fixtures do the same.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
§9.1 -- List<BK> back-references
Documented and model-checked as valid, but with no read path: elements are decoded
into descriptors of the referencing entity, so a key collection has nothing to
decode into and `decodeDescriptor` dereferenced a null descriptor. The model check
and the schema check disagreed about whether the shape was legal at all. Now
rejected, with the fix named; CrudBackRef's KDoc and the descriptor plan no longer
offer it. Implementing it stays open and is appealing -- a single-leaf List<BK>
would need no composite type, just a text[].

§6.3 -- native enum columns
A Postgres enum column generates its own EnumType class, which no CommonMappers
entry bridges (the shipped pair converts a *text* column). The finding said only
"not compatible"; it now says the column type is unsupported and what to do about
it. The index advisory in the same section stays open -- like §4.6 it needs a
severity on Finding so it can report without failing startup.

Plan updated: §2-§6 marked fixed where they landed, with the two open items and
why they are decisions rather than work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correctness, from the spec axis:
- JooqConfigurer.descriptorCells resolved only the descriptor's own conventional
  UDT name, but verification now accepts a view's descriptor stored as its base's
  type. That combination left the cells empty and decoded a base-stored value
  *unconverted* - an enum arriving as raw text. It now walks the view chain the
  same way verification does.
- Dropped the id identity check: jOOQ's `identity()` is equally true of `serial`
  and GENERATED BY DEFAULT, both of which accept the supplied value, so it fired
  on schemas that work - and its own advice ("use GENERATED BY DEFAULT") would not
  have silenced it. Nothing in the metadata separates ALWAYS from BY DEFAULT.

Standards axis:
- ASCII-only: 45 authored lines carried en/em dashes or ellipsis characters.
  Pre-existing prose in the same files is left byte-for-byte.
- One case-insensitive `udt(name)` lookup replaces three call sites that disagreed
  about case (`udtsByName[...]` vs an `equals(ignoreCase = true)` scan), plus one
  `componentUdtName` for the repeated qualified-type-name strip.
- Split a 163-char line; renamed the `nullability` fixture to `note`, since four
  column-level checks now share it and only one is about nullability; corrected a
  fixture KDoc describing packages that do not exist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A descriptor's composite type was matched positionally by type alone, so a rename
on either side decoded values into the wrong field in silence - and a search into
an aggregated array, which addresses elements by attribute name, broke outright.
Names are now part of the contract and are verified.

The convention: a business-key leaf carries its whole flattening chain - the
snake_case of every property along the way, joined by `__`. The doubled separator
keeps the boundary between fields distinct from the `_` inside one field's own
name: SlotKey(part: PartKey, slotNo) over PartKey(code) gives `part__code` and
`slot_no`, which a single separator could not tell apart. A descriptive field is
simply its own snake_case name.

The shared test schema had `book_descriptor.author_email` for a leaf three
properties deep (author -> email -> email); a migration renames it to
`author__email__email` and the jOOQ sources are regenerated (only BookDescriptor
and UDTs kept - the rest of the regeneration was pure re-indentation).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(verification): dbManaged fields, descriptor view id type, CrudService* exemptions
All checks were successful
/ release (pull_request) Successful in 2m45s
9cffb22de3
Three refinements to the model verification, all opt-in and inert for existing models.

@CrudField(dbManaged = true) declares that the database owns a column (default, trigger,
generated) - a last_change and the like. Such a field is left out of the insert, exempt from
the "NOT NULL needs a default" nullability check, and counts as bookkeeping rather than an
attribute for the RELATION purity rule, so a link entity carrying only id, version and a
dbManaged column beside its two keys keeps plain RELATION. The rule now also names the
attributes that force RELATION_DESTRUCTIVE.

A descriptor's backing view had its id column checked for existence only, though the companion
join compares it to the parent's foreign key - a view id of another type passed startup and
failed at query time. It is now type-checked like a view entity's id against its base.

An operation a CrudService* implementation replaces never reaches the repository, so it no
longer has to satisfy the default path: the jOOQ write-target check reports only the write
operations still unreplaced (making the documented writable-view recipe verify cleanly), and a
CrudServiceUpdate exempts its entity's back-reference collections from the SET_NULL / RELATION
write-mode guardrails. Placement, element type and via stay checked - those serve the read path.
CrudServiceReplacements became a bean so both verifications can see it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three pieces, all verified at startup and compiled the same way in both storage shapes.

Descriptive references: a descriptor's descriptive field may now be a reference - a bare BK or
another entity's descriptor - instead of only a scalar. It is stored the way a reference inside a
business key is stored, flattened into the referent's key leaves and `__`-chained under its own
property name, and it materializes key-only, so the flattening stays one level deep. Both read paths
follow: the composite decoder consumes each field's share of the cells, and the flat read selects the
leaf columns and rebuilds from them. A collection of references, and a reference to something that is
not a scanned entity, stay findings.

Search through a reference: a back-reference collection may sit behind reference hops
(`<ref>.<collection>[.<elementField>]`). Resolution hands the collection to the embedded-array branch
with the navigated owner, and a path ending on one gets the whole-element-key target rather than
degrading to a plain column - which used to pass verification silently and then compare an array
column against an id sub-select. The predicate walks the path to the table that owns the array, so it
is unnested on the joined alias.

UDT shape check: the descriptor's expected attributes are built in one walk carrying name and type
together, so a field that flattens to a different number of attributes than it contributes types can
no longer desynchronize the comparison (it threw IndexOutOfBounds).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A descriptive field of a back-reference element may itself be a reference. A bare BK already took the
leaf-wise path; a `Descriptor`-typed one fell into the plain-value branch, where it filled only the
first of its leaves - and where an unresolved referent left no attribute at all, so the leaf-wise AND
degenerated to `true` and the EXISTS matched every row with a non-empty collection, whatever the
search value was. Both now go through the keyed branch, which throws on an unresolved referent
instead of answering with nonsense.

Found by code review of 4f9e652.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two defects found by code review of d3712a6.

A `List<Descriptor>` is a back-reference by its very shape, and the column behind it holds an
aggregate the database computes from *other* rows - but nothing said so on the write side. Its
`referencedType` is wired now, so an insert ran the whole list through the reference writer and threw
"neither a business key nor a descriptor"; that hit every entity declaring such a collection, even
carrying nothing but the empty list its default gives it. Insert now leaves the column alone, and a
patch aiming at the collection is refused with a message naming it instead of writing the wrong side.
On the read side a column holding no array at all was decoded as an empty list - indistinguishable
from "no elements" for a field that used to be read through a value mapper - and is now reported.

The OpenAPI key schema dereferenced its entity info unconditionally, while an entity with every REST
operation off deliberately has none. Rendering a reference to such an entity therefore threw, and
`enrich` catches per entity - so one unexposed referent silently dropped the *referring* entity's
paths from the spec. It falls back to the plain schema of the key class.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(descriptors): Added severity to validations
All checks were successful
/ release (pull_request) Successful in 2m46s
e5f99ecf9f
feat(descriptors): Deep search for descriptors
All checks were successful
/ release (pull_request) Successful in 3m2s
8cf5602432
feat(descriptors): Deep search for descriptors
All checks were successful
/ release (pull_request) Successful in 2m58s
46acd5978a
chore: fixes little issue with creating correct key
All checks were successful
/ release (pull_request) Successful in 2m46s
6f8df33cfe
feat(descriptors): Verification for orphaned descriptors
All checks were successful
/ release (pull_request) Successful in 2m55s
3e37bd1b04
feat(descriptors): Type mapper verification
All checks were successful
/ release (pull_request) Successful in 2m53s
2c4ffbf614
fix: String to enum parsing
All checks were successful
/ release (pull_request) Successful in 2m52s
002bf0626d
fix: Improve OpenAPI for write operations, BK or URI
All checks were successful
/ release (pull_request) Successful in 2m58s
76b704f157
@ -47,0 +62,4 @@
// unresolved for verification to report - never dropped on the quiet.
val found = findSubclasses<Descriptor<*, *>>(*basePackages.toTypedArray()).associateWith { it.descriptorEntityType() }
unresolvedDescriptors = found.filterValues { it == null || it !in byEntityType }
descriptors = found.mapNotNull { (dClass, entityType) -> entityType?.let { byEntityType[it] }?.let { dClass.metaDescriptor(it) } }

If elements returning null inside of mapNotNull they should appear in unresolvedDescriptors?

If elements returning null inside of mapNotNull they should appear in unresolvedDescriptors?
christian.keller marked this conversation as resolved
@ -47,0 +66,4 @@
// One descriptor per entity is enforced by verification; until the model is fixed the one first by class name
// is wired, so the same model behaves the same on every run.
descriptors.groupBy { it.entityType }.forEach { (entityType, ds) ->
byEntityType[entityType]?.descriptor = ds.minBy { it.type.name }

Shouldd be there not a mechanism to have more control over this? and min by type name should only be default

Shouldd be there not a mechanism to have more control over this? and min by type name should only be default
Author
Owner

Only one descriptor per Entity is allowed right now. This code is here to be able to verify a violation

Only one descriptor per Entity is allowed right now. This code is here to be able to verify a violation
christian.keller marked this conversation as resolved
fix: Review findings
All checks were successful
/ release (pull_request) Successful in 2m50s
fb646359cd
christian.keller deleted branch feat/descriptor 2026-08-18 10:09:48 +00:00
christian.keller referenced this pull request from a commit 2026-08-18 10:09:49 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
DenktMit-OSS/lt-crudular!35
No description provided.