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

danhaywood pushed a commit to branch CAUSEWAY-3676
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/CAUSEWAY-3676 by this push:
     new 2fb16249da CAUSEWAY-3676: improves handling of abstract input types
2fb16249da is described below

commit 2fb16249da3e11596ca51eb050caa4fbfaf807e4
Author: danhaywood <[email protected]>
AuthorDate: Tue Feb 20 07:16:08 2024 +0000

    CAUSEWAY-3676: improves handling of abstract input types
    
    ... can now specify concrete type when needed using 'logicalTypeName'
---
 .../DomainObjectAnnotationFacetFactory.java        |   2 +-
 .../viewer/graphql/model/domain/GqlvAction.java    |  72 ++++--
 .../model/domain/GqlvActionInvokeArgsArg.java      |   6 -
 .../graphql/model/domain/GqlvDomainObject.java     |  40 ++-
 .../model/domain/GqlvMutationForAction.java        |  33 ++-
 .../model/domain/GqlvMutationForProperty.java      |  32 ++-
 .../graphql/model/domain/GqlvScenarioStep.java     |  14 +-
 .../graphql/model/toplevel/GqlvTopLevelQuery.java  |  16 +-
 .../graphql/viewer/test/domain/dept/DeptHead.java  |   2 +-
 .../graphql/viewer/test/domain/dept/People.java    |  36 +++
 .../graphql/viewer/test/domain/dept/Person.java    |  17 ++
 .../viewer/test/domain/dept/StaffMember.java       |   2 +-
 ...alculator_IntegTest.each.add_big_decimals._.gql |   9 +
 .../e2e/People_IntegTest.each.find_person._.gql    |  31 +++
 ...People_IntegTest.each.find_person.approved.json |  33 +++
 .../graphql/viewer/test/e2e/People_IntegTest.java  |  72 ++++++
 ...Test.each.name_of_when_dept_head_using_id._.gql |  14 +
 ...h.name_of_when_dept_head_using_id.approved.json |  27 ++
 ...est.each.name_of_when_dept_head_using_ref._.gql |  33 +++
 ....name_of_when_dept_head_using_ref.approved.json |  35 +++
 ...ame_of_when_staff_member_using_invalid_id._.gql |  14 +
 ...hen_staff_member_using_invalid_id.approved.json |  27 ++
 ....each.name_of_when_staff_member_using_ref._.gql |  33 +++
 ...me_of_when_staff_member_using_ref.approved.json |  35 +++
 .../graphql/viewer/test/e2e/Person_IntegTest.java  |  72 ++++++
 .../e2e/special/DeptHeadMutating_IntegTest.java    |   1 -
 ...ting_IntegTest.java => Person_2_IntegTest.java} |  31 ++-
 ...me_of_person_using_id_and_logicalTypeName._.gql |   7 +
 ...rson_using_id_and_logicalTypeName.approved.json |   9 +
 ...rson_using_id_but_invalid_logicalTypeName._.gql |   7 +
 ...ng_id_but_invalid_logicalTypeName.approved.json |  16 ++
 viewers/graphql/test/src/test/resources/schema.gql | 282 +++++++++++++++++++++
 32 files changed, 1004 insertions(+), 56 deletions(-)

diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
index 612a61c28e..3fbe85e74a 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
@@ -118,7 +118,7 @@ implements
             final Optional<DomainObject> domainObjectIfAny,
             final ProcessObjectTypeContext processClassContext) {
 
-        if(!domainObjectIfAny.isPresent()) {
+        if(domainObjectIfAny.isEmpty()) {
             return;
         }
 
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvAction.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvAction.java
index f03611501a..73d84df5ce 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvAction.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvAction.java
@@ -130,6 +130,8 @@ public class GqlvAction
                 .map(oap -> {
                     final ObjectSpecification elementType = 
oap.getElementType();
                     Object argumentValue = argumentPojos.get(oap.getId());
+                    Object pojoOrPojoList;
+
                     switch (elementType.getBeanSort()) {
 
                         case VALUE:
@@ -140,20 +142,23 @@ public class GqlvAction
                             if (argumentValue == null) {
                                 return ManagedObject.empty(elementType);
                             }
-                            Object pojoOrPojoList;
+                            // fall through
+
+                        case ABSTRACT:
+                            // if the parameter is abstract, we still attempt 
to figure out the arguments.
+                            // the arguments will need to either use 'ref' or 
else both 'id' AND 'logicalTypeName'
                             if (argumentValue instanceof List) {
                                 val argumentValueList = (List<Object>) 
argumentValue;
                                 pojoOrPojoList = argumentValueList.stream()
-                                        .map(value -> 
asPojo(oap.getElementType(), value, context.bookmarkService, environment))
+                                        .map(value -> 
asPojo(oap.getElementType(), value, environment, context))
                                         .filter(Optional::isPresent)
                                         .map(Optional::get)
                                         .collect(Collectors.toList());
                             } else {
-                                pojoOrPojoList = asPojo(oap.getElementType(), 
argumentValue, context.bookmarkService, environment).orElse(null);
+                                pojoOrPojoList = asPojo(oap.getElementType(), 
argumentValue, environment, context).orElse(null);
                             }
                             return ManagedObject.adaptParameter(oap, 
pojoOrPojoList);
 
-                        case ABSTRACT:
                         case COLLECTION:
                         case MANAGED_BEAN_CONTRIBUTING:
                         case VETOED:
@@ -185,27 +190,64 @@ public class GqlvAction
     public static Optional<Object> asPojo(
             final ObjectSpecification elementType,
             final Object argumentValueObj,
-            final BookmarkService bookmarkService,
-            final Environment environment) {
+            final Environment environment,
+            final Context context
+    ) {
         val argumentValue = (Map<String, String>) argumentValueObj;
-        String idValue = argumentValue.get("id");
+
+        val refValue = argumentValue.get("ref");
+        if (refValue != null) {
+            String key = GqlvMetaSaveAs.keyFor(refValue);
+            BookmarkedPojo bookmarkedPojo = 
environment.getGraphQlContext().get(key);
+            if (bookmarkedPojo == null) {
+                throw new IllegalArgumentException(String.format(
+                    "Could not find object referenced '%s' in the execution 
context; was it saved previously using \"saveAs\" ?", refValue));
+            }
+            val targetPojoClass = bookmarkedPojo.getTargetPojo().getClass();
+            val targetPojoSpec = 
context.specificationLoader.loadSpecification(targetPojoClass);
+            if (targetPojoSpec == null) {
+                throw new IllegalArgumentException(String.format(
+                    "The object referenced '%s' is not part of the metamodel 
(has class '%s')",
+                    refValue, targetPojoClass.getCanonicalName()));
+            }
+            if (!elementType.isPojoCompatible(bookmarkedPojo.getTargetPojo())) 
{
+                throw new IllegalArgumentException(String.format(
+                    "The object referenced '%s' has a type '%s' that is not 
assignable to the required type '%s'",
+                    refValue, targetPojoSpec.getLogicalTypeName(), 
elementType.getLogicalTypeName()));
+            }
+            return 
Optional.of(bookmarkedPojo).map(BookmarkedPojo::getTargetPojo);
+        }
+
+        val idValue = argumentValue.get("id");
         if (idValue != null) {
             Class<?> paramClass = elementType.getCorrespondingClass();
-            Optional<Bookmark> bookmarkIfAny = 
bookmarkService.bookmarkFor(paramClass, idValue);
+            Optional<Bookmark> bookmarkIfAny;
+            if(elementType.isAbstract()) {
+                val logicalTypeName = argumentValue.get("logicalTypeName");
+                if (logicalTypeName == null) {
+                    throw new IllegalArgumentException(String.format(
+                            "The 'logicalTypeName' is required along with the 
'id', because the input type '%s' is abstract",
+                            elementType.getLogicalTypeName()));
+                }
+                
if(context.specificationLoader.specForLogicalTypeName(logicalTypeName).isEmpty())
 {
+                    throw new IllegalArgumentException(String.format(
+                            "The 'logicalTypeName' of '%s' is unknown in the 
metamodel",
+                            logicalTypeName));
+                }
+
+                 bookmarkIfAny = 
Optional.of(Bookmark.forLogicalTypeNameAndIdentifier(logicalTypeName, idValue));
+            } else {
+                bookmarkIfAny = 
context.bookmarkService.bookmarkFor(paramClass, idValue);
+            }
             return bookmarkIfAny
-                    .map(bookmarkService::lookup)
+                    .map(context.bookmarkService::lookup)
                     .filter(Optional::isPresent)
                     .map(Optional::get);
         }
-        String refValue = argumentValue.get("ref");
-        if (refValue != null) {
-            String key = GqlvMetaSaveAs.keyFor(refValue);
-            BookmarkedPojo value = environment.getGraphQlContext().get(key);
-            return Optional.of(value).map(BookmarkedPojo::getTargetPojo);
-        }
         throw new IllegalArgumentException("Either 'id' or 'ref' must be 
specified for a DomainObject input type");
     }
 
+
     public void addGqlArguments(
             final ObjectAction objectAction,
             final GraphQLFieldDefinition.Builder builder,
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionInvokeArgsArg.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionInvokeArgsArg.java
index f46436a4d1..f5627da21e 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionInvokeArgsArg.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionInvokeArgsArg.java
@@ -26,8 +26,6 @@ import 
org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectActionParameter;
 import org.apache.causeway.viewer.graphql.model.context.Context;
-import org.apache.causeway.viewer.graphql.model.domain.GqlvAbstract;
-import org.apache.causeway.viewer.graphql.model.fetcher.BookmarkedPojo;
 import 
org.apache.causeway.viewer.graphql.model.mmproviders.ObjectActionProvider;
 import 
org.apache.causeway.viewer.graphql.model.mmproviders.ObjectSpecificationProvider;
 
@@ -37,12 +35,8 @@ import lombok.Getter;
 import lombok.extern.log4j.Log4j2;
 import lombok.val;
 
-import java.util.Map;
-
 import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
 
-import static 
org.apache.causeway.viewer.graphql.model.domain.GqlvAction.asPojo;
-
 @Log4j2
 public class GqlvActionInvokeArgsArg
         extends GqlvAbstract {
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvDomainObject.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvDomainObject.java
index 64f200eb2a..cc4849cca0 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvDomainObject.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvDomainObject.java
@@ -60,7 +60,35 @@ public class GqlvDomainObject
     public static GqlvDomainObject of(
             final ObjectSpecification objectSpecification,
             final Context context) {
-        return context.domainObjectBySpec.computeIfAbsent(objectSpecification, 
spec -> new GqlvDomainObject(spec, context));
+
+        mapSuperclassesIfNecessary(objectSpecification, context);
+
+        return computeIfAbsentGqlvDomainObject(context, objectSpecification);
+    }
+
+    private static void mapSuperclassesIfNecessary(
+            final ObjectSpecification objectSpecification,
+            final Context context) {
+        // no need to map if the target subclass has already been built
+        if(context.domainObjectBySpec.containsKey(objectSpecification)) {
+            return;
+        }
+        val superclasses = superclassesOf(objectSpecification);
+        superclasses.forEach(objectSpec -> 
computeIfAbsentGqlvDomainObject(context, objectSpec));
+    }
+
+    private static GqlvDomainObject computeIfAbsentGqlvDomainObject(Context 
context, ObjectSpecification objectSpec) {
+        return context.domainObjectBySpec.computeIfAbsent(objectSpec, spec -> 
new GqlvDomainObject(spec, context));
+    }
+
+    private static List<ObjectSpecification> superclassesOf(final 
ObjectSpecification objectSpecification) {
+        val superclasses = new ArrayList<ObjectSpecification>();
+        ObjectSpecification superclass = objectSpecification.superclass();
+        while (superclass != null && superclass.getCorrespondingClass() != 
Object.class) {
+            superclasses.add(0, superclass);
+            superclass = superclass.superclass();
+        }
+        return superclasses;
     }
 
     private GqlvDomainObject(
@@ -83,11 +111,19 @@ public class GqlvDomainObject
         inputObjectTypeBuilder
                 .field(newInputObjectField()
                         .name("id")
+                        .description("Use either 'id' or 'ref'; looks up an 
entity from the persistent data store, or if a view model, then recreates using 
the id as a memento of the object's state")
                         .type(Scalars.GraphQLID)
                         .build()
                 )
+                .field(newInputObjectField()
+                        .name("logicalTypeName")
+                        .description("If object identified by 'id', then 
optionally specifies concrete type.  This is only required if the parameter 
type defines a super class")
+                        .type(Scalars.GraphQLString)
+                        .build()
+                )
                 .field(newInputObjectField()
                         .name("ref")
+                        .description("Use either 'ref' or 'id'; looks up an 
object previously saved to the execution context using 'saveAs(ref: ...)'")
                         .type(Scalars.GraphQLString)
                         .build()
                 )
@@ -156,7 +192,7 @@ public class GqlvDomainObject
     @Override
     protected Object fetchData(DataFetchingEnvironment 
dataFetchingEnvironment) {
         Object target = dataFetchingEnvironment.getArgument("object");
-        return GqlvAction.asPojo(getObjectSpecification(), target, 
this.context.bookmarkService, new Environment.For(dataFetchingEnvironment))
+        return GqlvAction.asPojo(getObjectSpecification(), target, new 
Environment.For(dataFetchingEnvironment), context)
                 .orElse(null);
     }
 
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutationForAction.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutationForAction.java
index 3a92c863b1..3f89482512 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutationForAction.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutationForAction.java
@@ -19,6 +19,8 @@
 package org.apache.causeway.viewer.graphql.model.domain;
 
 import java.util.ArrayList;
+import java.util.Map;
+import java.util.Optional;
 
 import graphql.schema.DataFetchingEnvironment;
 import graphql.schema.GraphQLArgument;
@@ -29,6 +31,9 @@ import graphql.schema.GraphQLType;
 
 import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
 
+import org.apache.causeway.applib.services.bookmark.Bookmark;
+import org.apache.causeway.viewer.graphql.model.fetcher.BookmarkedPojo;
+
 import org.springframework.lang.Nullable;
 
 import org.apache.causeway.applib.annotation.Where;
@@ -126,7 +131,33 @@ public class GqlvMutationForAction extends GqlvAbstract {
             sourcePojo = 
context.serviceRegistry.lookupServiceElseFail(objectSpec.getCorrespondingClass());
         } else {
             Object target = dataFetchingEnvironment.getArgument(argumentName);
-            sourcePojo = GqlvAction.asPojo(objectSpec, target, 
context.bookmarkService, environment)
+            Optional<Object> result;
+            val argumentValue = (Map<String, String>) target;
+            String idValue = argumentValue.get("id");
+            if (idValue != null) {
+                String logicalTypeName = argumentValue.get("logicalTypeName");
+                Optional<Bookmark> bookmarkIfAny;
+                if (logicalTypeName != null) {
+                    bookmarkIfAny = 
Optional.of(Bookmark.forLogicalTypeNameAndIdentifier(logicalTypeName, idValue));
+                } else {
+                    Class<?> paramClass = objectSpec.getCorrespondingClass();
+                    bookmarkIfAny = 
context.bookmarkService.bookmarkFor(paramClass, idValue);
+                }
+                result = bookmarkIfAny
+                        .map(context.bookmarkService::lookup)
+                        .filter(Optional::isPresent)
+                        .map(Optional::get);
+            } else {
+                String refValue = argumentValue.get("ref");
+                if (refValue != null) {
+                    String key = GqlvMetaSaveAs.keyFor(refValue);
+                    BookmarkedPojo value = ((Environment) 
environment).getGraphQlContext().get(key);
+                    result = 
Optional.of(value).map(BookmarkedPojo::getTargetPojo);
+                } else {
+                    throw new IllegalArgumentException("Either 'id' or 'ref' 
must be specified for a DomainObject input type");
+                }
+            }
+            sourcePojo = result
                     .orElseThrow(); // TODO: better error handling if no such 
object found.
         }
 
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutationForProperty.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutationForProperty.java
index 5320b48702..e4c6502436 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutationForProperty.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutationForProperty.java
@@ -19,6 +19,7 @@
 package org.apache.causeway.viewer.graphql.model.domain;
 
 import java.util.Map;
+import java.util.Optional;
 
 import graphql.schema.DataFetchingEnvironment;
 import graphql.schema.GraphQLArgument;
@@ -28,6 +29,7 @@ import graphql.schema.GraphQLOutputType;
 import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
 
 import org.apache.causeway.applib.annotation.Where;
+import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
@@ -36,6 +38,7 @@ import 
org.apache.causeway.viewer.graphql.model.context.Context;
 import org.apache.causeway.viewer.graphql.model.exceptions.DisabledException;
 import org.apache.causeway.viewer.graphql.model.exceptions.HiddenException;
 import org.apache.causeway.viewer.graphql.model.exceptions.InvalidException;
+import org.apache.causeway.viewer.graphql.model.fetcher.BookmarkedPojo;
 import org.apache.causeway.viewer.graphql.model.types.TypeMapper;
 
 import lombok.val;
@@ -80,7 +83,34 @@ public class GqlvMutationForProperty extends GqlvAbstract {
 
 
         Object target = dataFetchingEnvironment.getArgument(argumentName);
-        Object sourcePojo = GqlvAction.asPojo(objectSpec, target, 
context.bookmarkService, new Environment.For(dataFetchingEnvironment))
+        Optional<Object> result;
+        final Environment environment = new 
Environment.For(dataFetchingEnvironment);
+        val argumentValue1 = (Map<String, String>) target;
+        String idValue = argumentValue1.get("id");
+        if (idValue != null) {
+            String logicalTypeName = argumentValue1.get("logicalTypeName");
+            Optional<Bookmark> bookmarkIfAny;
+            if (logicalTypeName != null) {
+                bookmarkIfAny = 
Optional.of(Bookmark.forLogicalTypeNameAndIdentifier(logicalTypeName, idValue));
+            } else {
+                Class<?> paramClass = objectSpec.getCorrespondingClass();
+                bookmarkIfAny = 
context.bookmarkService.bookmarkFor(paramClass, idValue);
+            }
+            result = bookmarkIfAny
+                    .map(context.bookmarkService::lookup)
+                    .filter(Optional::isPresent)
+                    .map(Optional::get);
+        } else {
+            String refValue = argumentValue1.get("ref");
+            if (refValue != null) {
+                String key = GqlvMetaSaveAs.keyFor(refValue);
+                BookmarkedPojo value = 
environment.getGraphQlContext().get(key);
+                result = Optional.of(value).map(BookmarkedPojo::getTargetPojo);
+            } else {
+                throw new IllegalArgumentException("Either 'id' or 'ref' must 
be specified for a DomainObject input type");
+            }
+        }
+        Object sourcePojo = result
                     .orElseThrow(); // TODO: better error handling if no such 
object found.
 
         val managedObject = ManagedObject.adaptSingular(objectSpec, 
sourcePojo);
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvScenarioStep.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvScenarioStep.java
index 7b9718e774..d0826236bd 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvScenarioStep.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvScenarioStep.java
@@ -25,6 +25,7 @@ public class GqlvScenarioStep
             return;
         }
 
+        // add domain object lookup to top-level query
         context.objectSpecifications().forEach(objectSpec -> {
             switch (objectSpec.getBeanSort()) {
 
@@ -32,7 +33,7 @@ public class GqlvScenarioStep
                 case VIEW_MODEL: // @DomainObject(nature=VIEW_MODEL)
                 case ENTITY:     // @DomainObject(nature=ENTITY)
 
-                    domainObjects.add(GqlvDomainObject.of(objectSpec, 
context));
+                    
domainObjects.add(addChildFieldFor(GqlvDomainObject.of(objectSpec, context)));
 
                     break;
             }
@@ -41,19 +42,10 @@ public class GqlvScenarioStep
         context.objectSpecifications().forEach(objectSpec -> {
             if (Objects.requireNonNull(objectSpec.getBeanSort()) == 
BeanSort.MANAGED_BEAN_CONTRIBUTING) { // @DomainService
                 
context.serviceRegistry.lookupBeanById(objectSpec.getLogicalTypeName())
-                        .ifPresent(servicePojo -> {
-                            val gqlvDomainService = 
GqlvDomainService.of(objectSpec, servicePojo, context);
-                            addChildFieldFor(gqlvDomainService);
-                            domainServices.add(gqlvDomainService);
-                        });
+                        .ifPresent(servicePojo -> 
domainServices.add(addChildFieldFor(GqlvDomainService.of(objectSpec, 
servicePojo, context))));
             }
         });
 
-        // add domain object lookup to top-level query
-        for (val gqlvDomainObject : this.domainObjects) {
-            addChildFieldFor(gqlvDomainObject);
-        }
-
         buildObjectType();
     }
 
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/toplevel/GqlvTopLevelQuery.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/toplevel/GqlvTopLevelQuery.java
index 0dee8a582a..ad1308039f 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/toplevel/GqlvTopLevelQuery.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/toplevel/GqlvTopLevelQuery.java
@@ -27,6 +27,7 @@ public class GqlvTopLevelQuery
     public GqlvTopLevelQuery(final Context context) {
         super("Query", context);
 
+        // add domain object lookup to top-level query
         context.objectSpecifications().forEach(objectSpec -> {
             switch (objectSpec.getBeanSort()) {
 
@@ -34,7 +35,7 @@ public class GqlvTopLevelQuery
                 case VIEW_MODEL: // @DomainObject(nature=VIEW_MODEL)
                 case ENTITY:     // @DomainObject(nature=ENTITY)
 
-                    domainObjects.add(GqlvDomainObject.of(objectSpec, 
context));
+                    
domainObjects.add(addChildFieldFor(GqlvDomainObject.of(objectSpec, context)));
 
                     break;
             }
@@ -45,20 +46,13 @@ public class GqlvTopLevelQuery
             switch (objectSpec.getBeanSort()) {
                 case MANAGED_BEAN_CONTRIBUTING: // @DomainService
                     
context.serviceRegistry.lookupBeanById(objectSpec.getLogicalTypeName())
-                            .ifPresent(servicePojo -> {
-                                val gqlvDomainService = 
GqlvDomainService.of(objectSpec, servicePojo, context);
-                                addChildFieldFor(gqlvDomainService);
-                                domainServices.add(gqlvDomainService);
-                            });
+                            .ifPresent(servicePojo ->
+                                    domainServices.add(
+                                            
addChildFieldFor(GqlvDomainService.of(objectSpec, servicePojo, context))));
                     break;
             }
         });
 
-        // add domain object lookup to top-level query
-        for (val gqlvDomainObject : this.domainObjects) {
-            addChildFieldFor(gqlvDomainObject);
-        }
-
         addChildFieldFor(scenario = new GqlvScenario(context));
 
         buildObjectType();
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/DeptHead.java
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/DeptHead.java
index 12ed53631a..00694e37e8 100644
--- 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/DeptHead.java
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/DeptHead.java
@@ -58,7 +58,7 @@ import lombok.Setter;
 )
 @DomainObjectLayout(describedAs = "Departmental head, responsible for 
curriculum, research, funding and staff")
 @NoArgsConstructor
-public class DeptHead implements Comparable<DeptHead> {
+public class DeptHead extends Person implements Comparable<DeptHead>  {
 
     public DeptHead(String name) {
         this.name = name;
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/People.java
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/People.java
new file mode 100644
index 0000000000..197815aa63
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/People.java
@@ -0,0 +1,36 @@
+package org.apache.causeway.viewer.graphql.viewer.test.domain.dept;
+
+import lombok.RequiredArgsConstructor;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.causeway.applib.annotation.Action;
+import org.apache.causeway.applib.annotation.DomainService;
+import org.apache.causeway.applib.annotation.NatureOfService;
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+import org.apache.causeway.applib.annotation.SemanticsOf;
+
+@Named("university.dept.People")
+@DomainService(
+        nature= NatureOfService.VIEW)
[email protected](PriorityPrecedence.EARLY)
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+public class People {
+
+    private final StaffMemberRepository staffMemberRepository;
+    private final DeptHeadRepository deptHeadRepository;
+
+    @Action(semantics = SemanticsOf.SAFE)
+    public Person findNamed(String name) {
+        return 
Optional.ofNullable((Person)staffMemberRepository.findByName(name))
+                .orElse(deptHeadRepository.findByName(name));
+    }
+
+    @Action(semantics = SemanticsOf.SAFE)
+    public String nameOf(Person person) {
+        return person.getName();
+    }
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/Person.java
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/Person.java
new file mode 100644
index 0000000000..612d71ab71
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/Person.java
@@ -0,0 +1,17 @@
+package org.apache.causeway.viewer.graphql.viewer.test.domain.dept;
+
+import javax.inject.Named;
+import javax.persistence.MappedSuperclass;
+
+import org.apache.causeway.applib.annotation.DomainObject;
+import org.apache.causeway.applib.annotation.Nature;
+import org.apache.causeway.applib.annotation.Property;
+
+@MappedSuperclass
+@Named("university.dept.Person")
+@DomainObject(nature = Nature.NOT_SPECIFIED)
+public abstract class Person {
+
+    @Property
+    public abstract String getName();
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/StaffMember.java
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/StaffMember.java
index a8e471699c..1813f9fd8e 100644
--- 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/StaffMember.java
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/dept/StaffMember.java
@@ -54,7 +54,7 @@ import lombok.Setter;
 @DomainObject(nature = Nature.ENTITY, autoCompleteRepository = 
StaffMemberRepository.class, autoCompleteMethod = "findByNameMatching")
 @DomainObjectLayout(describedAs = "Staff member of a university department, 
responsible for delivering lectures, tutorials, exam invigilation and candidate 
interviews")
 @NoArgsConstructor
-public class StaffMember implements Comparable<StaffMember> {
+public class StaffMember extends Person implements Comparable<StaffMember> {
 
     public StaffMember(
             final String name,
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Calculator_IntegTest.each.add_big_decimals._.gql
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Calculator_IntegTest.each.add_big_decimals._.gql
index 5bb92ef25d..3b022461fb 100644
--- 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Calculator_IntegTest.each.add_big_decimals._.gql
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Calculator_IntegTest.each.add_big_decimals._.gql
@@ -9,5 +9,14 @@
         results
       }
     }
+    addBigDecimals {
+      invoke(x: "1.1", y: "2.2") {
+        args {
+          x
+          y
+        }
+        results
+      }
+    }
   }
 }
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.each.find_person._.gql
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.each.find_person._.gql
new file mode 100644
index 0000000000..4effad8393
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.each.find_person._.gql
@@ -0,0 +1,31 @@
+{
+  Scenario(name: "DeptHead is also a Person") {
+    Name
+    Given {
+      university_dept_People {
+        findNamed {
+          invoke(name: "Dr. Helen Johansen") {
+            args {
+              name
+            }
+            results {
+              name {
+                get
+              }
+              _meta {
+                saveAs(ref: "dept-head")
+              }
+            }
+          }
+        }
+      }
+    }
+    When {
+      university_dept_Person(object: {ref: "dept-head"}) {
+        name {
+          get
+        }
+      }
+    }
+  }
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.each.find_person.approved.json
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.each.find_person.approved.json
new file mode 100644
index 0000000000..1079e5c78d
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.each.find_person.approved.json
@@ -0,0 +1,33 @@
+{
+  "data" : {
+    "Scenario" : {
+      "Name" : "DeptHead is also a Person",
+      "Given" : {
+        "university_dept_People" : {
+          "findNamed" : {
+            "invoke" : {
+              "args" : {
+                "name" : "Dr. Helen Johansen"
+              },
+              "results" : {
+                "name" : {
+                  "get" : "Dr. Helen Johansen"
+                },
+                "_meta" : {
+                  "saveAs" : "dept-head"
+                }
+              }
+            }
+          }
+        }
+      },
+      "When" : {
+        "university_dept_Person" : {
+          "name" : {
+            "get" : "Dr. Helen Johansen"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.java
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.java
new file mode 100644
index 0000000000..f5b031bc83
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/People_IntegTest.java
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.viewer.graphql.viewer.test.e2e;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Collectors;
+
+import org.approvaltests.Approvals;
+import org.approvaltests.integrations.junit5.JupiterApprovals;
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.TestFactory;
+
+import org.springframework.test.context.ActiveProfiles;
+
+import lombok.val;
+
+
+//NOT USING @Transactional since we are running server within same transaction 
otherwise
+@Order(50)
+@ActiveProfiles("test")
+public class People_IntegTest extends Abstract_IntegTest {
+
+    @TestFactory
+    Iterable<DynamicTest> each() throws IOException, URISyntaxException {
+
+        val integClassName = getClass().getSimpleName();
+        val classUrl = getClass().getResource(integClassName + ".class");
+        Path classPath = Paths.get(classUrl.toURI());
+        Path directoryPath = classPath.getParent();
+
+        return Files.walk(directoryPath)
+                .filter(Files::isRegularFile)
+                .filter(file -> {
+                    String fileName = file.getFileName().toString();
+                    return fileName.startsWith(integClassName) && 
fileName.endsWith("._.gql");
+                })
+                .map(file -> {
+                    String fileName = file.getFileName().toString();
+                    String testName = 
fileName.substring(integClassName.length() + 
".each.".length()).replace("._.gql", "");
+                    return JupiterApprovals.dynamicTest(
+                            testName,
+                            options -> {
+                                Approvals.verify(submitFileNamed(fileName), 
jsonOptions(options));
+                                afterEach();
+                                beforeEach();
+                            });
+                })
+                .collect(Collectors.toList());
+    }
+
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_id._.gql
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_id._.gql
new file mode 100644
index 0000000000..507b900e46
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_id._.gql
@@ -0,0 +1,14 @@
+{
+  Scenario(name: "Obtain name of person but forget to specify the 
logicalTypeName") {
+    Name
+    When {
+      university_dept_People {
+        nameOf {
+          invoke(person: {id: "123"}) {
+            results
+          }
+        }
+      }
+    }
+  }
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_id.approved.json
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_id.approved.json
new file mode 100644
index 0000000000..7bb8f1b44e
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_id.approved.json
@@ -0,0 +1,27 @@
+{
+  "errors" : [ {
+    "message" : "Exception while fetching data 
(/Scenario/When/university_dept_People/nameOf/invoke/results) : The 
'logicalTypeName' is required along with the 'id', because the input type 
'university.dept.Person' is abstract",
+    "locations" : [ {
+      "line" : 8,
+      "column" : 13
+    } ],
+    "path" : [ "Scenario", "When", "university_dept_People", "nameOf", 
"invoke", "results" ],
+    "extensions" : {
+      "classification" : "DataFetchingException"
+    }
+  } ],
+  "data" : {
+    "Scenario" : {
+      "Name" : "Obtain name of person but forget to specify the 
logicalTypeName",
+      "When" : {
+        "university_dept_People" : {
+          "nameOf" : {
+            "invoke" : {
+              "results" : null
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_ref._.gql
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_ref._.gql
new file mode 100644
index 0000000000..2fb8d4beee
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_ref._.gql
@@ -0,0 +1,33 @@
+{
+  Scenario(name: "Obtain name of person that's a DeptHead") {
+    Name
+    Given {
+      university_dept_DeptHeads {
+        findHeadByName {
+          invoke(name: "Dr. Helen Johansen") {
+            args {
+              name
+            }
+            results {
+              name {
+                get
+              }
+              _meta {
+                saveAs(ref: "dept-head")
+              }
+            }
+          }
+        }
+      }
+    }
+    When {
+      university_dept_People {
+        nameOf {
+          invoke(person: {ref: "dept-head"}) {
+            results
+          }
+        }
+      }
+    }
+  }
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_ref.approved.json
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_ref.approved.json
new file mode 100644
index 0000000000..2ac1a93c81
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_dept_head_using_ref.approved.json
@@ -0,0 +1,35 @@
+{
+  "data" : {
+    "Scenario" : {
+      "Name" : "Obtain name of person that's a DeptHead",
+      "Given" : {
+        "university_dept_DeptHeads" : {
+          "findHeadByName" : {
+            "invoke" : {
+              "args" : {
+                "name" : "Dr. Helen Johansen"
+              },
+              "results" : {
+                "name" : {
+                  "get" : "Dr. Helen Johansen"
+                },
+                "_meta" : {
+                  "saveAs" : "dept-head"
+                }
+              }
+            }
+          }
+        }
+      },
+      "When" : {
+        "university_dept_People" : {
+          "nameOf" : {
+            "invoke" : {
+              "results" : "Dr. Helen Johansen"
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_invalid_id._.gql
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_invalid_id._.gql
new file mode 100644
index 0000000000..eae4185397
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_invalid_id._.gql
@@ -0,0 +1,14 @@
+{
+  Scenario(name: "Obtain name of person that's a non-existent StaffMember") {
+    Name
+    When {
+      university_dept_People {
+        nameOf {
+          invoke(person: {id: "123456", logicalTypeName: 
"university.dept.StaffMember"}) {
+            results
+          }
+        }
+      }
+    }
+  }
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_invalid_id.approved.json
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_invalid_id.approved.json
new file mode 100644
index 0000000000..b8b5fe2be9
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_invalid_id.approved.json
@@ -0,0 +1,27 @@
+{
+  "errors" : [ {
+    "message" : "Exception while fetching data 
(/Scenario/When/university_dept_People/nameOf/invoke/results) : 'Person' is 
mandatory",
+    "locations" : [ {
+      "line" : 8,
+      "column" : 13
+    } ],
+    "path" : [ "Scenario", "When", "university_dept_People", "nameOf", 
"invoke", "results" ],
+    "extensions" : {
+      "classification" : "DataFetchingException"
+    }
+  } ],
+  "data" : {
+    "Scenario" : {
+      "Name" : "Obtain name of person that's a non-existent StaffMember",
+      "When" : {
+        "university_dept_People" : {
+          "nameOf" : {
+            "invoke" : {
+              "results" : null
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_ref._.gql
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_ref._.gql
new file mode 100644
index 0000000000..a993291aef
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_ref._.gql
@@ -0,0 +1,33 @@
+{
+  Scenario(name: "Obtain name of person that's a StaffMember") {
+    Name
+    Given {
+      university_dept_Staff {
+        findStaffMemberByName {
+          invoke(name: "Letitia Leadbetter") {
+            args {
+              name
+            }
+            results {
+              name {
+                get
+              }
+              _meta {
+                saveAs(ref: "staff-member")
+              }
+            }
+          }
+        }
+      }
+    }
+    When {
+      university_dept_People {
+        nameOf {
+          invoke(person: {ref: "staff-member"}) {
+            results
+          }
+        }
+      }
+    }
+  }
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_ref.approved.json
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_ref.approved.json
new file mode 100644
index 0000000000..d189aa83bb
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.each.name_of_when_staff_member_using_ref.approved.json
@@ -0,0 +1,35 @@
+{
+  "data" : {
+    "Scenario" : {
+      "Name" : "Obtain name of person that's a StaffMember",
+      "Given" : {
+        "university_dept_Staff" : {
+          "findStaffMemberByName" : {
+            "invoke" : {
+              "args" : {
+                "name" : "Letitia Leadbetter"
+              },
+              "results" : {
+                "name" : {
+                  "get" : "Letitia Leadbetter"
+                },
+                "_meta" : {
+                  "saveAs" : "staff-member"
+                }
+              }
+            }
+          }
+        }
+      },
+      "When" : {
+        "university_dept_People" : {
+          "nameOf" : {
+            "invoke" : {
+              "results" : "Letitia Leadbetter"
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.java
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.java
new file mode 100644
index 0000000000..403a9e0087
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Person_IntegTest.java
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.viewer.graphql.viewer.test.e2e;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Collectors;
+
+import org.approvaltests.Approvals;
+import org.approvaltests.integrations.junit5.JupiterApprovals;
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.TestFactory;
+
+import org.springframework.test.context.ActiveProfiles;
+
+import lombok.val;
+
+
+//NOT USING @Transactional since we are running server within same transaction 
otherwise
+@Order(50)
+@ActiveProfiles("test")
+public class Person_IntegTest extends Abstract_IntegTest {
+
+    @TestFactory
+    Iterable<DynamicTest> each() throws IOException, URISyntaxException {
+
+        val integClassName = getClass().getSimpleName();
+        val classUrl = getClass().getResource(integClassName + ".class");
+        Path classPath = Paths.get(classUrl.toURI());
+        Path directoryPath = classPath.getParent();
+
+        return Files.walk(directoryPath)
+                .filter(Files::isRegularFile)
+                .filter(file -> {
+                    String fileName = file.getFileName().toString();
+                    return fileName.startsWith(integClassName) && 
fileName.endsWith("._.gql");
+                })
+                .map(file -> {
+                    String fileName = file.getFileName().toString();
+                    String testName = 
fileName.substring(integClassName.length() + 
".each.".length()).replace("._.gql", "");
+                    return JupiterApprovals.dynamicTest(
+                            testName,
+                            options -> {
+                                Approvals.verify(submitFileNamed(fileName), 
jsonOptions(options));
+                                afterEach();
+                                beforeEach();
+                            });
+                })
+                .collect(Collectors.toList());
+    }
+
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/DeptHeadMutating_IntegTest.java
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/DeptHeadMutating_IntegTest.java
index abd2185aa5..f60514e5bc 100644
--- 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/DeptHeadMutating_IntegTest.java
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/DeptHeadMutating_IntegTest.java
@@ -60,6 +60,5 @@ public class DeptHeadMutating_IntegTest extends 
Abstract_IntegTest {
 
         // then payload
         Approvals.verify(response, jsonOptions());
-
     }
 }
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/DeptHeadMutating_IntegTest.java
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.java
similarity index 63%
copy from 
viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/DeptHeadMutating_IntegTest.java
copy to 
viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.java
index abd2185aa5..5752a5d6af 100644
--- 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/DeptHeadMutating_IntegTest.java
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.java
@@ -20,6 +20,8 @@ package 
org.apache.causeway.viewer.graphql.viewer.test.e2e.special;
 
 import java.util.Optional;
 
+import org.apache.causeway.viewer.graphql.viewer.test.domain.dept.StaffMember;
+
 import org.approvaltests.Approvals;
 import org.approvaltests.reporters.DiffReporter;
 import org.approvaltests.reporters.UseReporter;
@@ -40,26 +42,45 @@ import lombok.val;
 //NOT USING @Transactional since we are running server within same transaction 
otherwise
 @Order(120)
 @ActiveProfiles("test")
-public class DeptHeadMutating_IntegTest extends Abstract_IntegTest {
+public class Person_2_IntegTest extends Abstract_IntegTest {
 
     @Test
     @UseReporter(DiffReporter.class)
-    void change_department_name() throws Exception {
+    void name_of_person_using_id_and_logicalTypeName() throws Exception {
 
         final Bookmark bookmark =
                 transactionService.callTransactional(
                         Propagation.REQUIRED,
                         () -> {
-                            Department department = 
departmentRepository.findByName("Classics");
-                            Optional<Bookmark> bookmark1 = 
bookmarkService.bookmarkFor(department);
+                            StaffMember staffMember = 
staffMemberRepository.findByName("Letitia Leadbetter");
+                            Optional<Bookmark> bookmark1 = 
bookmarkService.bookmarkFor(staffMember);
                             return bookmark1.orElseThrow();
                         }
                 ).valueAsNonNullElseFail();
 
-        val response = submit(_Maps.unmodifiable("$departmentId", 
bookmark.getIdentifier()));
+        val response = submit(_Maps.unmodifiable("$staffMemberId", 
bookmark.getIdentifier()));
 
         // then payload
         Approvals.verify(response, jsonOptions());
+    }
+
+    @Test
+    @UseReporter(DiffReporter.class)
+    void name_of_person_using_id_but_invalid_logicalTypeName() throws 
Exception {
+
+        final Bookmark bookmark =
+                transactionService.callTransactional(
+                        Propagation.REQUIRED,
+                        () -> {
+                            StaffMember staffMember = 
staffMemberRepository.findByName("Letitia Leadbetter");
+                            Optional<Bookmark> bookmark1 = 
bookmarkService.bookmarkFor(staffMember);
+                            return bookmark1.orElseThrow();
+                        }
+                ).valueAsNonNullElseFail();
+
+        val response = submit(_Maps.unmodifiable("$staffMemberId", 
bookmark.getIdentifier()));
 
+        // then payload
+        Approvals.verify(response, jsonOptions());
     }
 }
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_and_logicalTypeName._.gql
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_and_logicalTypeName._.gql
new file mode 100644
index 0000000000..d390911377
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_and_logicalTypeName._.gql
@@ -0,0 +1,7 @@
+{
+  university_dept_Person(object: {id: "$staffMemberId", logicalTypeName: 
"university.dept.StaffMember"}) {
+    name {
+      get
+    }
+  }
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_and_logicalTypeName.approved.json
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_and_logicalTypeName.approved.json
new file mode 100644
index 0000000000..74cfd1d566
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_and_logicalTypeName.approved.json
@@ -0,0 +1,9 @@
+{
+  "data" : {
+    "university_dept_Person" : {
+      "name" : {
+        "get" : "Letitia Leadbetter"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_but_invalid_logicalTypeName._.gql
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_but_invalid_logicalTypeName._.gql
new file mode 100644
index 0000000000..7d26fdc242
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_but_invalid_logicalTypeName._.gql
@@ -0,0 +1,7 @@
+{
+  university_dept_Person(object: {id: "$staffMemberId", logicalTypeName: 
"university.dept.XXX"}) {
+    name {
+      get
+    }
+  }
+}
diff --git 
a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_but_invalid_logicalTypeName.approved.json
 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_but_invalid_logicalTypeName.approved.json
new file mode 100644
index 0000000000..66c31d82e8
--- /dev/null
+++ 
b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.name_of_person_using_id_but_invalid_logicalTypeName.approved.json
@@ -0,0 +1,16 @@
+{
+  "errors" : [ {
+    "message" : "Exception while fetching data (/university_dept_Person) : The 
'logicalTypeName' of 'university.dept.XXX' is unknown in the metamodel",
+    "locations" : [ {
+      "line" : 2,
+      "column" : 3
+    } ],
+    "path" : [ "university_dept_Person" ],
+    "extensions" : {
+      "classification" : "DataFetchingException"
+    }
+  } ],
+  "data" : {
+    "university_dept_Person" : null
+  }
+}
\ No newline at end of file
diff --git a/viewers/graphql/test/src/test/resources/schema.gql 
b/viewers/graphql/test/src/test/resources/schema.gql
index ece5374fe6..d8c8312fca 100644
--- a/viewers/graphql/test/src/test/resources/schema.gql
+++ b/viewers/graphql/test/src/test/resources/schema.gql
@@ -111,6 +111,8 @@ type Query {
   university_dept_Departments: university_dept_Departments
   university_dept_DeptHead(object: university_dept_DeptHead__gqlv_input): 
university_dept_DeptHead
   university_dept_DeptHeads: university_dept_DeptHeads
+  university_dept_People: university_dept_People
+  university_dept_Person(object: university_dept_Person__gqlv_input): 
university_dept_Person
   university_dept_Staff: university_dept_Staff
   university_dept_StaffMember(object: 
university_dept_StaffMember__gqlv_input): university_dept_StaffMember
 }
@@ -169,6 +171,8 @@ type ScenarioStep {
   university_dept_Departments: university_dept_Departments
   university_dept_DeptHead(object: university_dept_DeptHead__gqlv_input): 
university_dept_DeptHead
   university_dept_DeptHeads: university_dept_DeptHeads
+  university_dept_People: university_dept_People
+  university_dept_Person(object: university_dept_Person__gqlv_input): 
university_dept_Person
   university_dept_Staff: university_dept_Staff
   university_dept_StaffMember(object: 
university_dept_StaffMember__gqlv_input): university_dept_StaffMember
 }
@@ -1601,6 +1605,22 @@ type 
causeway_schema_metamodel_v2_DomainClassDto__service__gqlv_property {
   validate(service: Boolean): String
 }
 
+type causeway_schema_metamodel_v2_FacetHolder {
+  "Object metadata"
+  _meta: causeway_schema_metamodel_v2_FacetHolder__gqlv_meta
+}
+
+type causeway_schema_metamodel_v2_FacetHolder__gqlv_meta {
+  cssClass: String
+  grid: String
+  icon: String
+  id: String!
+  layout: String
+  logicalTypeName: String!
+  saveAs(ref: String): String
+  title: String!
+}
+
 type causeway_security_LoginRedirect {
   "Object metadata"
   _meta: causeway_security_LoginRedirect__gqlv_meta
@@ -3738,6 +3758,102 @@ type 
university_dept_DeptHeads__findHeadByName__name__gqlv_action_parameter {
   validity: String
 }
 
+type university_dept_People {
+  "Find Named"
+  findNamed: university_dept_People__findNamed__gqlv_action
+  "Name Of"
+  nameOf: university_dept_People__nameOf__gqlv_action
+}
+
+type university_dept_People__findNamed__gqlv_action {
+  disabled: String
+  hidden: Boolean
+  invoke(name: String!): university_dept_People__findNamed__gqlv_action_invoke
+  "Parameters of this action"
+  params: university_dept_People__findNamed__gqlv_action_params
+  validate(name: String): String
+}
+
+type university_dept_People__findNamed__gqlv_action_args {
+  name: String
+}
+
+type university_dept_People__findNamed__gqlv_action_invoke {
+  "Arguments used to invoke this action"
+  args: university_dept_People__findNamed__gqlv_action_args
+  results: university_dept_Person
+}
+
+type university_dept_People__findNamed__gqlv_action_params {
+  "Name"
+  name: university_dept_People__findNamed__name__gqlv_action_parameter
+}
+
+type university_dept_People__findNamed__name__gqlv_action_parameter {
+  datatype: String
+  disabled(name: String): String
+  hidden: Boolean
+  validity: String
+}
+
+type university_dept_People__nameOf__gqlv_action {
+  disabled: String
+  hidden: Boolean
+  invoke(person: university_dept_Person__gqlv_input!): 
university_dept_People__nameOf__gqlv_action_invoke
+  "Parameters of this action"
+  params: university_dept_People__nameOf__gqlv_action_params
+  validate(person: university_dept_Person__gqlv_input): String
+}
+
+type university_dept_People__nameOf__gqlv_action_args {
+  person: university_dept_Person
+}
+
+type university_dept_People__nameOf__gqlv_action_invoke {
+  "Arguments used to invoke this action"
+  args: university_dept_People__nameOf__gqlv_action_args
+  results: String
+}
+
+type university_dept_People__nameOf__gqlv_action_params {
+  "Person"
+  person: university_dept_People__nameOf__person__gqlv_action_parameter
+}
+
+type university_dept_People__nameOf__person__gqlv_action_parameter {
+  datatype: String
+  disabled(person: university_dept_Person__gqlv_input): String
+  hidden: Boolean
+  validity: String
+}
+
+type university_dept_Person {
+  "Object metadata"
+  _meta: university_dept_Person__gqlv_meta
+  "Name"
+  name: university_dept_Person__name__gqlv_property
+}
+
+type university_dept_Person__gqlv_meta {
+  cssClass: String
+  grid: String
+  icon: String
+  id: String!
+  layout: String
+  logicalTypeName: String!
+  saveAs(ref: String): String
+  title: String!
+}
+
+type university_dept_Person__name__gqlv_property {
+  datatype: String
+  disabled: String
+  get: String!
+  hidden: Boolean
+  set(name: String!): university_dept_Person
+  validate(name: String): String
+}
+
 type university_dept_Staff {
   "Staff member of a university department, responsible for delivering 
lectures, tutorials, exam invigilation and candidate interviews"
   createStaffMember: university_dept_Staff__createStaffMember__gqlv_action
@@ -3959,186 +4075,352 @@ scalar Time
 scalar UUID
 
 input causeway_applib_DomainObjectList__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_FacetGroupNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_ParameterNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_PropertyNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_RoleMemento__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_TypeNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_UserMemento__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_node_ActionNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_node_CollectionNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_node_FacetAttrNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_applib_node_FacetNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_conf_ConfigurationProperty__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_conf_ConfigurationViewmodel__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_feat_ApplicationFeatureViewModel__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_feat_ApplicationNamespace__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_feat_ApplicationTypeAction__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_feat_ApplicationTypeCollection__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_feat_ApplicationTypeMember__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_feat_ApplicationTypeProperty__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_feat_ApplicationType__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_schema_metamodel_v2_DomainClassDto__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
+  id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
+  ref: String
+}
+
+input causeway_schema_metamodel_v2_FacetHolder__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_security_LoginRedirect__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input causeway_testing_fixtures_FixtureResult__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input java_lang_Runnable__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input java_util_Map__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input java_util_SortedMap__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input java_util_concurrent_Callable__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input java_util_function_BiFunction__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input java_util_function_Consumer__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input java_util_function_Function__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input java_util_stream_Stream__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input org_apache_causeway_core_metamodel_inspect_model_MMNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input org_apache_causeway_core_metamodel_inspect_model_MemberNode__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input 
org_apache_causeway_testing_fixtures_applib_fixturescripts_FixtureScript__gqlv_input
 {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input university_dept_Department__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input university_dept_DeptHead__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
+  id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
+  ref: String
+}
+
+input university_dept_Person__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }
 
 input university_dept_StaffMember__gqlv_input {
+  "Use either 'id' or 'ref'; looks up an entity from the persistent data 
store, or if a view model, then recreates using the id as a memento of the 
object's state"
   id: ID
+  "If object identified by 'id', then optionally specifies concrete type.  
This is only required if the parameter type defines a super class"
+  logicalTypeName: String
+  "Use either 'ref' or 'id'; looks up an object previously saved to the 
execution context using 'saveAs(ref: ...)'"
   ref: String
 }

Reply via email to