This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3913-grid.fixes
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 32f8ef004ef3cc60a204d5f0da2e6b97e3ce97ef
Author: Andi Huber <[email protected]>
AuthorDate: Fri Oct 24 17:11:11 2025 +0200

    CAUSEWAY-2297: GraphQL layout resource fixes
---
 .../applib/layout/grid/bootstrap/BSTab.java        | 44 ++++++----------------
 .../applib/layout/grid/bootstrap/BSTabGroup.java   | 21 ++++-------
 .../facets/object/grid/BSGridTransformer.java      | 12 +++++-
 ...hPdfJsViewer_IntegTest.dump_facets.approved.xml |  4 +-
 ..._MixinDomain_IntegTest.dump_facets.approved.xml |  4 +-
 ...hPdfjsViewer_IntegTest.dump_facets.approved.xml |  2 +-
 ...r_PropDomain_IntegTest.dump_facets.approved.xml |  2 +-
 .../viewer/controller/ResourceController.java      | 35 ++++++++---------
 8 files changed, 51 insertions(+), 73 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSTab.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSTab.java
index 44146adf2fd..850ce8d835c 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSTab.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSTab.java
@@ -25,6 +25,9 @@
 import jakarta.xml.bind.annotation.XmlTransient;
 import jakarta.xml.bind.annotation.XmlType;
 
+import lombok.Getter;
+import lombok.Setter;
+
 /**
  * Represents a tab within a {@link BSTabGroup tab group}.
  *
@@ -39,46 +42,21 @@ public final class BSTab extends BSElementAbstract 
implements BSRowOwner {
 
     private static final long serialVersionUID = 1L;
 
+    @Getter(onMethod_ = {@XmlAttribute(required = true)})
+    @Setter
     private String name;
-    @XmlAttribute(required = true)
-    public String getName() {
-        return name;
-    }
-
-    public void setName(final String name) {
-        this.name = name;
-    }
 
+    @Getter(onMethod_ = {@XmlElement(name = "row", required = true)})
+    @Setter
     private List<BSRow> rows = new ArrayList<>();
 
-    // no wrapper
-    @Override
-    @XmlElement(name = "row", required = true)
-    public List<BSRow> getRows() {
-        return rows;
-    }
-
-    public void setRows(final List<BSRow> rows) {
-        this.rows = rows;
-    }
-
-    private BSTabOwner owner;
-
     /**
      * Owner.
-     *
-     * <p>
-     *     Set programmatically by framework after reading in from XML.
-     * </p>
+     * <p>Set programmatically by framework after reading in from XML.
      */
-    @XmlTransient
-    public BSTabOwner getOwner() {
-        return owner;
-    }
-
-    public void setOwner(final BSTabOwner owner) {
-        this.owner = owner;
-    }
+    @Getter(onMethod_ = {@XmlTransient})
+    @Setter
+    private BSTabOwner owner;
 
     @Override public String toString() {
         return "BSTab{" +
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSTabGroup.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSTabGroup.java
index 3d508a5f3bd..957d76525be 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSTabGroup.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSTabGroup.java
@@ -26,6 +26,9 @@
 import jakarta.xml.bind.annotation.XmlTransient;
 import jakarta.xml.bind.annotation.XmlType;
 
+import lombok.Getter;
+import lombok.Setter;
+
 /**
  * Represents a tab group containing one or more {@link BSTab tab}s.
  *
@@ -83,23 +86,13 @@ public void setTabs(final List<BSTab> tabs) {
         this.tabs = tabs;
     }
 
-    private BSTabGroupOwner owner;
-
     /**
      * Owner.
-     *
-     * <p>
-     *     Set programmatically by framework after reading in from XML.
-     * </p>
+     * <p>Set programmatically by framework after reading in from XML.
      */
-    @XmlTransient
-    public BSTabGroupOwner getOwner() {
-        return owner;
-    }
-
-    public void setOwner(final BSTabGroupOwner owner) {
-        this.owner = owner;
-    }
+    @Getter(onMethod_ = {@XmlTransient})
+    @Setter
+    private BSTabGroupOwner owner;
 
     private String metadataError;
 
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/BSGridTransformer.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/BSGridTransformer.java
index ac4fdf4c1de..28d70c5ad08 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/BSGridTransformer.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/BSGridTransformer.java
@@ -84,7 +84,17 @@ private void keep() {
                 }
             });
 
-            emptyTabs.forEach(tab->tab.getOwner().getTabs().remove(tab));
+            emptyTabs.forEach(tab->{
+                    var tabGroup = (BSTabGroup)tab.getOwner();
+                    tab.setOwner(null); //fully detach
+                    if(tabGroup==null) return;
+                    tabGroup.getTabs().remove(tab);
+                    // remove empty tab-groups as well
+                    if(tabGroup.getTabs().isEmpty()) {
+                        var tabGroupOwner = tabGroup.getOwner();
+                        tabGroupOwner.getTabGroups().remove(tabGroup);
+                    }
+                });
 
             return bsGrid;
         }
diff --git 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_MixinDomainWithPdfJsViewer_IntegTest.dump_facets.approved.xml
 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_MixinDomainWithPdfJsViewer_IntegTest.dump_facets.approved.xml
index 805c9827f0f..c168ed44a33 100644
--- 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_MixinDomainWithPdfJsViewer_IntegTest.dump_facets.approved.xml
+++ 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_MixinDomainWithPdfJsViewer_IntegTest.dump_facets.approved.xml
@@ -56,7 +56,7 @@
         <mml:attr name="precedence" value="DEFAULT"/>
         <mml:attr name="value" 
value="org.apache.causeway.applib.events.lifecycle.ObjectUpdatingEvent.Default"/>
       </mml:facet>
-      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.GridFacetDefault">
+      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.BSGridFacet">
         <mml:attr name="precedence" value="DEFAULT"/>
       </mml:facet>
       <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacetFromAuthorization">
@@ -1174,7 +1174,7 @@
         <mml:attr name="precedence" value="DEFAULT"/>
         <mml:attr name="value" 
value="org.apache.causeway.applib.events.lifecycle.ObjectUpdatingEvent.Default"/>
       </mml:facet>
-      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.GridFacetDefault">
+      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.BSGridFacet">
         <mml:attr name="precedence" value="DEFAULT"/>
       </mml:facet>
       <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacetFromAuthorization">
diff --git 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_MixinDomain_IntegTest.dump_facets.approved.xml
 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_MixinDomain_IntegTest.dump_facets.approved.xml
index ad9eed94f0b..a6deefbaf4b 100644
--- 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_MixinDomain_IntegTest.dump_facets.approved.xml
+++ 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_MixinDomain_IntegTest.dump_facets.approved.xml
@@ -56,7 +56,7 @@
         <mml:attr name="precedence" value="DEFAULT"/>
         <mml:attr name="value" 
value="org.apache.causeway.applib.events.lifecycle.ObjectUpdatingEvent.Default"/>
       </mml:facet>
-      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.GridFacetDefault">
+      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.BSGridFacet">
         <mml:attr name="precedence" value="DEFAULT"/>
       </mml:facet>
       <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacetFromAuthorization">
@@ -1167,7 +1167,7 @@
         <mml:attr name="precedence" value="DEFAULT"/>
         <mml:attr name="value" 
value="org.apache.causeway.applib.events.lifecycle.ObjectUpdatingEvent.Default"/>
       </mml:facet>
-      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.GridFacetDefault">
+      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.BSGridFacet">
         <mml:attr name="precedence" value="DEFAULT"/>
       </mml:facet>
       <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacetFromAuthorization">
diff --git 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_PropDomainWithPdfjsViewer_IntegTest.dump_facets.approved.xml
 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_PropDomainWithPdfjsViewer_IntegTest.dump_facets.approved.xml
index d6d3af4adcc..f4f13678c05 100644
--- 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_PropDomainWithPdfjsViewer_IntegTest.dump_facets.approved.xml
+++ 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_PropDomainWithPdfjsViewer_IntegTest.dump_facets.approved.xml
@@ -56,7 +56,7 @@
         <mml:attr name="precedence" value="DEFAULT"/>
         <mml:attr name="value" 
value="org.apache.causeway.applib.events.lifecycle.ObjectUpdatingEvent.Default"/>
       </mml:facet>
-      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.GridFacetDefault">
+      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.BSGridFacet">
         <mml:attr name="precedence" value="DEFAULT"/>
       </mml:facet>
       <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacetFromAuthorization">
diff --git 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_PropDomain_IntegTest.dump_facets.approved.xml
 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_PropDomain_IntegTest.dump_facets.approved.xml
index e1a27bdbd64..961410098c1 100644
--- 
a/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_PropDomain_IntegTest.dump_facets.approved.xml
+++ 
b/extensions/vw/pdfjs/metamodel/src/test/java/org/apache/causeway/extensions/pdfjs/metamodel/PdfjsViewer_PropDomain_IntegTest.dump_facets.approved.xml
@@ -56,7 +56,7 @@
         <mml:attr name="precedence" value="DEFAULT"/>
         <mml:attr name="value" 
value="org.apache.causeway.applib.events.lifecycle.ObjectUpdatingEvent.Default"/>
       </mml:facet>
-      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.GridFacetDefault">
+      <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.grid.GridFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.grid.BSGridFacet">
         <mml:attr name="precedence" value="DEFAULT"/>
       </mml:facet>
       <mml:facet 
id="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacet" 
fqcn="org.apache.causeway.core.metamodel.facets.object.hidden.HiddenTypeFacetFromAuthorization">
diff --git 
a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
 
b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
index 9712078bb80..b74d74b5cf7 100644
--- 
a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
+++ 
b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/ResourceController.java
@@ -23,8 +23,6 @@
 
 import jakarta.inject.Inject;
 
-import org.jspecify.annotations.Nullable;
-
 import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
@@ -37,12 +35,13 @@
 import org.springframework.web.bind.annotation.RestController;
 
 import org.apache.causeway.applib.annotation.ObjectSupport.IconSize;
-import org.apache.causeway.applib.layout.grid.Grid;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.applib.services.bookmark.BookmarkService;
+import org.apache.causeway.applib.services.grid.GridService;
 import org.apache.causeway.applib.value.Blob;
 import org.apache.causeway.applib.value.Clob;
-import org.apache.causeway.commons.io.JaxbUtils;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.core.metamodel.facets.object.grid.GridFacet;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
@@ -51,20 +50,19 @@
 
 @RestController()
 @RequestMapping("/graphql/object")
-public class ResourceController {
-
-    private final BookmarkService bookmarkService;
-    private final ObjectManager objectManager;
-    private final CausewayConfiguration.Viewer.Graphql graphqlConfiguration;
+public record ResourceController(
+    BookmarkService bookmarkService,
+    ObjectManager objectManager,
+    GridService gridService,
+    CausewayConfiguration.Viewer.Graphql graphqlConfiguration) {
 
     @Inject
     public ResourceController(
             final BookmarkService bookmarkService,
             final ObjectManager objectManager,
+            final GridService gridService,
             final CausewayConfiguration causewayConfiguration) {
-        this.bookmarkService = bookmarkService;
-        this.objectManager = objectManager;
-        this.graphqlConfiguration = causewayConfiguration.viewer().graphql();
+        this(bookmarkService, objectManager, gridService, 
causewayConfiguration.viewer().graphql());
     }
 
     @GetMapping(value = "/{logicalTypeName}:{id}/{propertyId}/blobBytes")
@@ -142,10 +140,8 @@ public ResponseEntity<String> grid(
         }
 
         return lookup(logicalTypeName, id)
-                .map(ResourceController::gridOf)
+                .map(mo->gridAsXml(mo).orElse(null))
                 .filter(Objects::nonNull)
-                .map(JaxbUtils::toStringUtf8)
-                .map(x -> x.replaceAll("(\r\n)", "\n"))
                 .map(gridText -> {
                     var bodyBuilder = ResponseEntity.ok()
                             .contentType(MediaType.APPLICATION_XML);
@@ -191,10 +187,11 @@ public ResponseEntity<byte[]> icon(
                 .orElse(ResponseEntity.notFound().build());
     }
 
-    @Nullable
-    private static Grid gridOf(final ManagedObject managedObject) {
-        var facet = managedObject.objSpec().getFacet(GridFacet.class);
-        return facet != null ? facet.getGrid(managedObject) : null;
+    private Optional<String> gridAsXml(final ManagedObject managedObject) {
+        return managedObject.objSpec().lookupFacet(GridFacet.class)
+            .map(facet->facet.getGrid(managedObject))
+            
.map(grid->gridService().marshaller().marshal(_Casts.uncheckedCast(grid), 
CommonMimeType.XML))
+            .map(x -> x.replaceAll("(\r\n)", "\n"));
     }
 
     private Optional<Object> valueOfProperty(final String logicalTypeName, 
final String id, final String propertyId) {

Reply via email to