Skip to content

intent: a .print reaches a cross-model relation's own relation, and an audited document's CreatedBy / UpdatedBy (#7752, #8609) - #8614

Merged
delchev merged 2 commits into
masterfrom
issue-7752-print-feeder-depth
Nov 13, 2026
Merged

delchev merged 2 commits into
masterfrom
issue-7752-print-feeder-depth

Conversation

@delchev

@delchev delchev commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Two gaps in the payload a generated <Entity>PrintFeeder hands a .print, both in the same generator code. One commit each.

#7752 - a cross-model relation's own relation was unreachable

Cause. A record of another model is copied onto the payload reflectively (copyRecordFields, #6422), so its own to-one relations arrive as bare keys: document.Customer.City was 1, and {{document.Customer.City.Name}} rendered empty.

Design: the owner declares its references, the consumer follows them at runtime.

  • Entity.java.template (dao-java) puts a new SDK annotation on every to-one foreign-key field of a generated entity: @org.eclipse.dirigible.sdk.db.Reference(repository = "gen.countries.data.settings.CityRepository", label = "Name"). The store does not read it; persistence is unchanged.
  • The feeder calls the new org.eclipse.dirigible.sdk.print.PrintRecords.expandReferences(record, map, loadedReferences) right after copyRecordFields. It reads the annotations off the loaded record's class, loads each referenced record through its repository (so the language overlay applies) and replaces the key with that record's fields plus __label.
  • PrintFeederSupport only flags the node (expandReferences); it emits no node for the owner's relations and names none of them.

Why not the two simpler routes. Reading the owner's relation graph from its .model at generation time and emitting a node per relation compiles customer.City and gen.countries...CityRepository into the consumer - the #6422 failure again: the owner drops or retargets a relation, the never-regenerated consumer stops compiling, and the all-or-nothing client-Java batch takes every module down. Baking the same names as strings and loading reflectively compiles forever but is still a snapshot of another model's schema in the consumer's gen/. With the annotation the relation graph is written exactly once, by its owner, in code that is regenerated with it.

Bounds and cost.

  • Exactly one hop: the referenced record's own references stay keys (PrintRecords never recurses).
  • Only for a cross-model relation of the document itself or of a line item. A cross-model record reached through a same-model relation (already depth 2) is not expanded.
  • One findById per distinct (repository, key) per payload. The cache is shared by the header relations and all item rows, and remembers misses, so there is no load per row. A print pays about one extra read per set to-one relation of each cross-model record it carries (a customer with City, Country, Currency, Kind set: four reads).

What changes for existing modules.

  • Regenerated entities gain the annotation on their to-one fields (source diff only).
  • Regenerated feeders with a cross-model relation gain the PrintRecords call and the cache map.
  • A relation key of a cross-model record becomes a node: a bare {{document.Customer.City}} now prints the city's label where it printed the integer key.
  • Both sides must be generated on a platform that has this. An owner generated earlier carries no @Reference, so its records keep their keys, with no error. A reference whose repository class is absent or whose record is gone also keeps its key (logged at WARN).

#8609 - an audit: true document carried no audit columns

Cause. The four audit columns are injected by EdmIntentGenerator.auditProperties, they are not authored fields, and the feeder's rootScalars are built from the authored fields only.

Change. For an audited document master PrintFeederSupport appends CreatedAt, CreatedBy, UpdatedAt, UpdatedBy (the names the generated entity declares) to the root scalars; the two timestamps print as ISO instants. Root only - line items, relation nodes and non-audited documents are unchanged. UpdatedBy / UpdatedAt are absent from the JSON until the record is first updated (null keys are not serialised).

No CreatedByName / UpdatedByName. The platform has no display name to resolve: the tenant User is id, username, password and roles, and CreatedBy holds what UserFacade.getName() returned (the username, or the identity provider's principal). Left out rather than faked.

Verification

Run locally on this branch:

  • mvn install -pl components/api/api-modules-java,components/template/template-application-dao-java,components/template/template-application-events-java,components/ide/ide-template,components/engine/engine-intent - api-modules-java 130 tests, ide-template 359, engine-intent 2124, 0 failures. New: PrintRecordsTest (6), three tests in PrintFeederSupportTest.
  • Runtime: IntentCrossModelFieldRetirementIT (extended) generates an owner and a consumer project, publishes both and reads the feeder over HTTP: document.Invoice.City.Name and document.Invoice.City.__label are Sofia from a consumer whose source names neither City nor its repository, and document.CreatedBy is admin with document.CreatedAt set.
  • Generated output compiled and run: mvn -P integration-tests -pl tests/tests-integrations verify -Dit.test=... -Dit.groups= -Dit.excludedGroups= for IntentEngineIT (83), ModelGenerationIT (1), IntentClickRoleGateIT (2), IntentExtraRolesIT (2), IntentCrossModuleCollisionIT (1), IntentEmissionCoverageIT (1) - all green. IntentEmissionCoverageIT timed out once in assertBpmEventsRuntime (the 180 s wait for a BPM expiry timer, State == EXPIRED) while running in the same batch, and passed when re-run with this branch unchanged; noted here in case CI shows the same.
  • Release-profile javadoc build (-P release -Dgpg.skip=true -DskipTests) of the five changed modules.
  • generate --check on the three sample-intent-* projects with the rebuilt application jar: no drift (none of them commits an entity with a relation or a print feeder), so there is no regenerated sample output in this PR.
  • mvn formatter:validate on the reactor with the formatter caches wiped.

Not run locally: the PostgreSQL leg and the full integration suite (CI).

Fixes #7752
Fixes #8609

🤖 Generated with Claude Code

delchev and others added 2 commits November 13, 2026 18:11
…cument.Customer.City.Name}} (#7752)

A record of another model is copied onto the print payload reflectively
(#6422), so its own to-one relations arrived as bare keys and
{{document.Customer.City.Name}} rendered empty.

The owner now declares its references and the consumer follows them at
runtime: the generated entity carries @reference(repository, label) on
every to-one foreign-key field, and the feeder asks the new SDK
PrintRecords.expandReferences to replace each such key with the referenced
record's fields and __label. Nothing of the owner's relation graph is
named in the consumer's generated source.

One hop only, for a cross-model relation of the document or of a line
item; one load per distinct (repository, key) per payload, shared by the
header relations and every item row.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…y / CreatedAt / UpdatedBy / UpdatedAt (#8609)

The audit columns are injected by the EDM generator, not authored, so the
print feeder - built from the authored fields - never put them and
{{document.CreatedBy}} rendered empty. An audited document master now gets
the four columns on `document`, under the names the generated entity
declares, the timestamps as ISO instants. Line items, relation nodes and
non-audited documents are unchanged.

No CreatedByName: the platform keeps no display name for a user.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
public static class CountryRepository {
static int loads;

public Object findById(Object id) {
@delchev
delchev merged commit 30defd3 into master Nov 13, 2026
16 checks passed
@delchev
delchev deleted the issue-7752-print-feeder-depth branch November 13, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants