CAUSEWAY-3676: improves handling of abstract input types
... can now specify concrete type when needed using 'logicalTypeName'
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 612a61c..3fbe85e 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 @@ private void validateConflictingTypeSemantics(
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 f036115..73d84df 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 static Can<ManagedObject> argumentManagedObjectsFor(
.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 static Can<ManagedObject> argumentManagedObjectsFor(
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 @@ private static ManagedObject adaptValue(
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 f46436a..f5627da 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.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.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 64f200e..cc4849c 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 @@ private 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 @@ protected void addDataFetchersForChildren() {
@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 3a92c86..3f89482 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 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 @@ protected Object fetchData(final DataFetchingEnvironment dataFetchingEnvironment
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 5320b48..e4c6502 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 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.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 @@ protected Object fetchData(final DataFetchingEnvironment dataFetchingEnvironment
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 7b9718e..d082623 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 GqlvScenarioStep(final Context context) {
return;
}
+ // add domain object lookup to top-level query
context.objectSpecifications().forEach(objectSpec -> {
switch (objectSpec.getBeanSort()) {
@@ -32,7 +33,7 @@ public GqlvScenarioStep(final Context context) {
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 GqlvScenarioStep(final Context context) {
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 0dee8a5..ad13080 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 GqlvTopLevelQuery(final Context context) {
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 GqlvTopLevelQuery(final Context context) {
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 12ed536..00694e3 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 @@
)
@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 0000000..197815a
--- /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)
+@javax.annotation.Priority(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 0000000..612d71a
--- /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 a8e4716..1813f9f 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 @@
@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 5bb92ef..3b02246 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 0000000..4effad8
--- /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 0000000..1079e5c
--- /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 0000000..f5b031b
--- /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 0000000..507b900
--- /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 0000000..7bb8f1b
--- /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 0000000..2fb8d4b
--- /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 0000000..2ac1a93
--- /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 0000000..eae4185
--- /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 0000000..b8b5fe2
--- /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 0000000..a993291
--- /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 0000000..d189aa8
--- /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 0000000..403a9e0
--- /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 abd2185..f60514e 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 @@ void change_department_name() throws Exception {
// 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.java b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.java
new file mode 100644
index 0000000..5752a5d
--- /dev/null
+++ b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/special/Person_2_IntegTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.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;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.transaction.annotation.Propagation;
+
+import org.apache.causeway.applib.services.bookmark.Bookmark;
+import org.apache.causeway.commons.internal.collections._Maps;
+import org.apache.causeway.viewer.graphql.viewer.test.domain.dept.Department;
+import org.apache.causeway.viewer.graphql.viewer.test.e2e.Abstract_IntegTest;
+
+import lombok.val;
+
+
+//NOT USING @Transactional since we are running server within same transaction otherwise
+@Order(120)
+@ActiveProfiles("test")
+public class Person_2_IntegTest extends Abstract_IntegTest {
+
+ @Test
+ @UseReporter(DiffReporter.class)
+ void name_of_person_using_id_and_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());
+ }
+
+ @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 0000000..d390911
--- /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 0000000..74cfd1d
--- /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 0000000..7d26fdc
--- /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 0000000..66c31d8
--- /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 ece5374..d8c8312 100644
--- a/viewers/graphql/test/src/test/resources/schema.gql
+++ b/viewers/graphql/test/src/test/resources/schema.gql
@@ -111,6 +111,8 @@
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 @@
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 @@
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 @@
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 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
}