From c50606c16f99b2a86c9071466e58ad71044fe094 Mon Sep 17 00:00:00 2001 From: delchev Date: Thu, 23 Jul 2026 00:11:53 +0300 Subject: [PATCH] feat(intent): honor major on to-one relations (off the list, still in the form) A relation's `major` attribute was silently ignored: RelationIntent had no `major` field and EdmIntentGenerator hardcoded widgetIsMajor="true" for every relation (both the local and the cross-model emitter). So `major: false` on a manyToOne/oneToOne parsed fine but never dropped the dropdown from the entity list / document-items table. Add `major` (default true) to RelationIntent and emit widgetIsMajor from it in relationProperty + crossModelRelationProperty. The Harmonia detail-register columns loop already filters on widgetIsMajor, so a `major: false` relation now renders in the create/edit form and detail pane but not as a list column - the relation counterpart of a field's `major: false`. Default true preserves all existing behavior; the attribute is opt-in. EdmIntentGeneratorTest covers a local and a cross-model major:false relation emitting widgetIsMajor=false while a default relation stays true. Co-Authored-By: Claude Opus 4.8 --- .../generator/edm/EdmIntentGenerator.java | 4 +-- .../intent/model/RelationIntent.java | 15 ++++++++++ .../generator/edm/EdmIntentGeneratorTest.java | 30 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/edm/EdmIntentGenerator.java b/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/edm/EdmIntentGenerator.java index 5abd425193..7c0132407d 100644 --- a/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/edm/EdmIntentGenerator.java +++ b/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/edm/EdmIntentGenerator.java @@ -1108,7 +1108,7 @@ private static Map relationProperty(String ownerEntity, Relation : relation.getSize() .toString()); p.put("widgetLength", "20"); - p.put("widgetIsMajor", "true"); + p.put("widgetIsMajor", relation.isMajor() ? "true" : "false"); p.put("widgetDropDownKey", keyFieldName(target)); p.put("widgetDropDownValue", labelFieldName(target)); putLookupColumns(p, relation); @@ -1157,7 +1157,7 @@ private static Map crossModelRelationProperty(String ownerEntity : relation.getSize() .toString()); p.put("widgetLength", "20"); - p.put("widgetIsMajor", "true"); + p.put("widgetIsMajor", relation.isMajor() ? "true" : "false"); p.put("widgetDropDownKey", info.keyField()); p.put("widgetDropDownValue", info.labelField()); putLookupColumns(p, relation); diff --git a/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/model/RelationIntent.java b/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/model/RelationIntent.java index d22d48e37e..77c319e438 100644 --- a/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/model/RelationIntent.java +++ b/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/model/RelationIntent.java @@ -65,6 +65,13 @@ public class RelationIntent { * width). Use a small span (e.g. 4) to pack several short dropdowns onto one row. */ private Integer size; + /** + * Whether this to-one relation shows as a column in the entity's list / document-items table + * ({@code widgetIsMajor}). Defaults to {@code true} (relations are list columns). Set + * {@code major: false} to keep the dropdown in the create/edit form and detail pane but off the + * list table - the relation counterpart of a field's {@code major: false}. + */ + private boolean major = true; /** * Optional list of the target entity's field names to surface as extra read-only columns * wherever this to-one relation shows as a lookup column (the master-detail / document allocation @@ -231,6 +238,14 @@ public void setSize(Integer size) { this.size = size; } + public boolean isMajor() { + return major; + } + + public void setMajor(boolean major) { + this.major = major; + } + public List getShow() { return show; } diff --git a/components/engine/engine-intent/src/test/java/org/eclipse/dirigible/components/intent/generator/edm/EdmIntentGeneratorTest.java b/components/engine/engine-intent/src/test/java/org/eclipse/dirigible/components/intent/generator/edm/EdmIntentGeneratorTest.java index 62b3a2c7a4..f373324402 100644 --- a/components/engine/engine-intent/src/test/java/org/eclipse/dirigible/components/intent/generator/edm/EdmIntentGeneratorTest.java +++ b/components/engine/engine-intent/src/test/java/org/eclipse/dirigible/components/intent/generator/edm/EdmIntentGeneratorTest.java @@ -342,6 +342,36 @@ void dependsOnEmitsWidgetAttributesWithPrimaryKeyDefaults() { assertNull(countryFk.get("widgetDependsOnProperty")); } + @Test + void relationMajorFalseEmitsWidgetIsMajorFalseAndDefaultsTrue() { + String yaml = """ + name: shop + uses: + - { model: products } + entities: + - name: Category + fields: + - { name: id, type: integer, primaryKey: true, generated: true } + - { name: name, type: string } + - name: OrderItem + fields: + - { name: id, type: integer, primaryKey: true, generated: true } + - { name: name, type: string } + relations: + - { name: Category, kind: manyToOne, to: Category } # default -> list column + - { name: Note, kind: manyToOne, to: Category, major: false } # local, off the list + - { name: Product, kind: manyToOne, to: Product, model: products, major: false } # cross-model, off the list + """; + IntentModel parsed = IntentParser.parse(yaml); + Map model = EdmIntentGenerator.buildModelJsonForTest(parsed, "shop"); + Map item = entityByName(entities(model), "OrderItem"); + + assertEquals("true", propertyByName(item, "Category").get("widgetIsMajor"), "a relation is a list column by default"); + assertEquals("false", propertyByName(item, "Note").get("widgetIsMajor"), "major:false keeps a local relation off the list table"); + assertEquals("false", propertyByName(item, "Product").get("widgetIsMajor"), + "major:false keeps a cross-model relation off the list table"); + } + @Test void documentMasterWithACalendarViewStillCarriesThePrintFlag() { // A document (header-items) master whose UI is overridden to a range calendar: the layout