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

commit 11221ffdd0f7b1a8a6632e0389db9bfd6331d15c
Author: danhaywood <[email protected]>
AuthorDate: Fri Jan 26 08:23:55 2024 +0000

    CAUSEWAY-3676: sketches param autocomplete; makes existing test 
deterministic
---
 .../graphql/model/domain/GqlvActionParam.java      |  11 +-
 .../model/domain/GqlvActionParamAutoComplete.java  | 127 +++++++++++++++++++++
 .../graphql/viewer/test/domain/Department.java     |  22 +++-
 ...t_and_remove_staff_member_choices.approved.json |   4 +-
 .../test/e2e/Schema_IntegTest.schema.approved.json |  57 +++++++++
 .../graphql/test/src/test/resources/schema.gql     |   2 +
 6 files changed, 214 insertions(+), 9 deletions(-)

diff --git 
a/incubator/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionParam.java
 
b/incubator/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionParam.java
index bda6cd8bd0..a01b6d37eb 100644
--- 
a/incubator/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionParam.java
+++ 
b/incubator/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionParam.java
@@ -43,6 +43,7 @@ public class GqlvActionParam
         implements GqlvActionParamValidate.Holder,
                    GqlvActionParamHidden.Holder,
                    GqlvActionParamChoices.Holder,
+                   GqlvActionParamAutoComplete.Holder,
                    GqlvActionParamDisabled.Holder {
 
     @Getter private final Holder holder;
@@ -53,10 +54,11 @@ public class GqlvActionParam
     private final GraphQLObjectType.Builder gqlObjectTypeBuilder;
     private final GraphQLObjectType gqlObjectType;
 
-    private final GqlvActionParamChoices choices;
     private final GqlvActionParamHidden hidden;
-    private final GqlvActionParamDisabled validate;
     private final GqlvActionParamValidate disabled;
+    private final GqlvActionParamChoices choices;
+    private final GqlvActionParamAutoComplete autoComplete;
+    private final GqlvActionParamDisabled validate;
 
     private final GraphQLFieldDefinition field;
 
@@ -75,6 +77,8 @@ public class GqlvActionParam
         this.disabled = new GqlvActionParamValidate(this, context);
         val choices = new GqlvActionParamChoices(this, context);
         this.choices = choices.hasChoices() ? choices : null;
+        val autoComplete = new GqlvActionParamAutoComplete(this, context);
+        this.autoComplete = autoComplete.hasAutoComplete() ? autoComplete : 
null;
         this.validate = new GqlvActionParamDisabled(this, context);
 
         this.gqlObjectType = gqlObjectTypeBuilder.build();
@@ -116,6 +120,9 @@ public class GqlvActionParam
         if (choices != null) {
             choices.addDataFetcher();
         }
+        if (autoComplete != null) {
+            autoComplete.addDataFetcher();
+        }
         validate.addDataFetcher();
     }
 
diff --git 
a/incubator/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionParamAutoComplete.java
 
b/incubator/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionParamAutoComplete.java
new file mode 100644
index 0000000000..444a240012
--- /dev/null
+++ 
b/incubator/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionParamAutoComplete.java
@@ -0,0 +1,127 @@
+ /*
+ *  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.model.domain;
+
+ import java.util.Collections;
+ import java.util.List;
+ import java.util.stream.Collectors;
+
+ import org.apache.causeway.applib.annotation.Where;
+ import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
+ import org.apache.causeway.core.metamodel.interactions.managed.ManagedAction;
+ import 
org.apache.causeway.core.metamodel.interactions.managed.ParameterNegotiationModel;
+ import org.apache.causeway.core.metamodel.object.ManagedObject;
+ import org.apache.causeway.viewer.graphql.model.context.Context;
+ import org.apache.causeway.viewer.graphql.model.fetcher.BookmarkedPojo;
+ import 
org.apache.causeway.viewer.graphql.model.mmproviders.ObjectActionParameterProvider;
+ import 
org.apache.causeway.viewer.graphql.model.mmproviders.ObjectActionProvider;
+ import 
org.apache.causeway.viewer.graphql.model.mmproviders.ObjectSpecificationProvider;
+ import org.apache.causeway.viewer.graphql.model.types.TypeMapper;
+
+ import static 
org.apache.causeway.viewer.graphql.model.domain.GqlvAction.addGqlArguments;
+
+ import graphql.schema.GraphQLArgument;
+
+ import lombok.val;
+ import lombok.extern.log4j.Log4j2;
+
+ import graphql.schema.DataFetchingEnvironment;
+ import graphql.schema.GraphQLFieldDefinition;
+ import graphql.schema.GraphQLList;
+
+ import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
+
+ @Log4j2
+ public class GqlvActionParamAutoComplete {
+
+     private static final String SEARCH_PARAM_NAME = "search";
+
+     private final Holder holder;
+     private final Context context;
+
+     /**
+      * Populated iff there is an autocomplete for this parameter.
+      */
+     private final GraphQLFieldDefinition field;
+
+     public GqlvActionParamAutoComplete(
+             final Holder holder,
+             final Context context) {
+         this.holder = holder;
+         this.context = context;
+
+         val objectActionParameter = holder.getObjectActionParameter();
+         if (objectActionParameter.hasAutoComplete()) {
+             val elementType = objectActionParameter.getElementType();
+             val fieldBuilder = newFieldDefinition()
+                     .name("autoComplete")
+                     
.type(GraphQLList.list(TypeMapper.outputTypeFor(elementType)));
+             addGqlArguments(holder.getObjectAction(), fieldBuilder, 
TypeMapper.InputContext.DISABLE, holder.getParamNum());
+             fieldBuilder.argument(GraphQLArgument.newArgument()
+                     .name(SEARCH_PARAM_NAME)
+                     .type(TypeMapper.scalarTypeFor(String.class)))
+                     .build();
+             this.field = holder.addField(fieldBuilder.build());
+         } else {
+             this.field = null;
+         }
+     }
+
+     boolean hasAutoComplete() {
+         return field != null;
+     }
+
+     public void addDataFetcher() {
+         context.codeRegistryBuilder.dataFetcher(
+                 holder.coordinatesFor(field),
+                 this::choices
+         );
+     }
+
+     private List<Object> choices(final DataFetchingEnvironment 
dataFetchingEnvironment) {
+
+         val sourcePojo = BookmarkedPojo.sourceFrom(dataFetchingEnvironment);
+         val objectSpecification = 
context.specificationLoader.loadSpecification(sourcePojo.getClass());
+         if (objectSpecification == null) {
+             return Collections.emptyList();
+         }
+
+         val objectAction = holder.getObjectAction();
+         val managedObject = ManagedObject.adaptSingular(objectSpecification, 
sourcePojo);
+
+         val objectActionParameter = 
objectAction.getParameterById(holder.getObjectActionParameter().getId());
+         val argumentManagedObjects = 
GqlvAction.argumentManagedObjectsFor(dataFetchingEnvironment, objectAction, 
context.bookmarkService);
+
+         val managedAction = ManagedAction.of(managedObject, objectAction, 
Where.ANYWHERE);
+         val pendingArgs = ParameterNegotiationModel.of(managedAction, 
argumentManagedObjects);
+         val autoCompleteChoices = 
objectActionParameter.getAutoComplete(pendingArgs, 
dataFetchingEnvironment.getArgument(SEARCH_PARAM_NAME), 
InteractionInitiatedBy.USER);
+
+         return autoCompleteChoices.stream()
+                    .map(ManagedObject::getPojo)
+                    .collect(Collectors.toList());
+     }
+
+     public interface Holder
+             extends GqlvHolder,
+             ObjectSpecificationProvider,
+             ObjectActionProvider,
+             ObjectActionParameterProvider {
+         GqlvActionParam.Holder getHolder();
+     }
+ }
diff --git 
a/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/Department.java
 
b/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/Department.java
index f5c90ec495..ac6046b59a 100644
--- 
a/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/Department.java
+++ 
b/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/domain/Department.java
@@ -41,9 +41,13 @@ import lombok.RequiredArgsConstructor;
 import lombok.Setter;
 import lombok.val;
 
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
 import java.util.Set;
+import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 @Entity
 @Table(
@@ -95,11 +99,15 @@ public class Department implements Comparable<Department> {
     private DeptHead deptHead;
 
 
-    @Getter @Setter
-    @Collection
     @OneToMany(mappedBy = "department")
     private Set<StaffMember> staffMembers = new TreeSet<>();
 
+    // because the ordering seems not to be deterministic?
+    @Collection
+    public List<StaffMember> getStaffMembers() {
+        return staffMembers.stream().sorted().collect(Collectors.toList());
+    }
+
     @Action(semantics = SemanticsOf.IDEMPOTENT)
     @ActionLayout(associateWith = "staffMembers")
     public class addStaffMember {
@@ -107,7 +115,7 @@ public class Department implements Comparable<Department> {
         public Department act(StaffMember staffMember) {
             val department = Department.this;
 
-            department.getStaffMembers().add(staffMember);
+            department.staffMembers.add(staffMember);
             staffMember.setDepartment(department);
             return department;
         }
@@ -125,8 +133,12 @@ public class Department implements Comparable<Department> {
             staffMember.setDepartment(department);
             return department;
         }
-        public Set<StaffMember> choices0Act() {
-            return Department.this.getStaffMembers();
+        public List<StaffMember> choices0Act() {
+            val department = Department.this;
+            return department.getStaffMembers()
+                        .stream()
+                        .sorted(Comparator.comparing(StaffMember::getName))
+                        .collect(Collectors.toList());
         }
     }
 
diff --git 
a/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Domain_IntegTest.find_department_and_remove_staff_member_choices.approved.json
 
b/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Domain_IntegTest.find_department_and_remove_staff_member_choices.approved.json
index 7d904e301f..e885150a5f 100644
--- 
a/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Domain_IntegTest.find_department_and_remove_staff_member_choices.approved.json
+++ 
b/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Domain_IntegTest.find_department_and_remove_staff_member_choices.approved.json
@@ -8,11 +8,11 @@
               "staffMember" : {
                 "choices" : [ {
                   "name" : {
-                    "get" : "Letitia Leadbetter"
+                    "get" : "Gerry Jones"
                   }
                 }, {
                   "name" : {
-                    "get" : "Gerry Jones"
+                    "get" : "Letitia Leadbetter"
                   }
                 } ]
               }
diff --git 
a/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Schema_IntegTest.schema.approved.json
 
b/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Schema_IntegTest.schema.approved.json
index 24f8bc2b9d..52c61ab6f8 100644
--- 
a/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Schema_IntegTest.schema.approved.json
+++ 
b/incubator/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/Schema_IntegTest.schema.approved.json
@@ -22824,6 +22824,30 @@
           },
           "isDeprecated" : false,
           "deprecationReason" : null
+        }, {
+          "name" : "autoComplete",
+          "description" : null,
+          "args" : [ {
+            "name" : "search",
+            "description" : null,
+            "type" : {
+              "kind" : "SCALAR",
+              "name" : "String",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          } ],
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "OBJECT",
+              "name" : "university_dept_StaffMember",
+              "ofType" : null
+            }
+          },
+          "isDeprecated" : false,
+          "deprecationReason" : null
         }, {
           "name" : "disabled",
           "description" : null,
@@ -23572,6 +23596,39 @@
           },
           "isDeprecated" : false,
           "deprecationReason" : null
+        }, {
+          "name" : "autoComplete",
+          "description" : null,
+          "args" : [ {
+            "name" : "name",
+            "description" : null,
+            "type" : {
+              "kind" : "SCALAR",
+              "name" : "String",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          }, {
+            "name" : "search",
+            "description" : null,
+            "type" : {
+              "kind" : "SCALAR",
+              "name" : "String",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          } ],
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "OBJECT",
+              "name" : "university_dept_DeptHead",
+              "ofType" : null
+            }
+          },
+          "isDeprecated" : false,
+          "deprecationReason" : null
         }, {
           "name" : "disabled",
           "description" : null,
diff --git a/incubator/viewers/graphql/test/src/test/resources/schema.gql 
b/incubator/viewers/graphql/test/src/test/resources/schema.gql
index 7a1410df17..250d82046b 100644
--- a/incubator/viewers/graphql/test/src/test/resources/schema.gql
+++ b/incubator/viewers/graphql/test/src/test/resources/schema.gql
@@ -2246,6 +2246,7 @@ type 
university_dept_Department__addStaffMember__gqlv_action_params {
 }
 
 type 
university_dept_Department__addStaffMember__staffMember__gqlv_action_parameter {
+  autoComplete(search: String): [university_dept_StaffMember]
   disabled(staffMember: university_dept_StaffMember__gqlv_input): String
   hidden: Boolean
   validity(staffMember: university_dept_StaffMember__gqlv_input): String
@@ -2323,6 +2324,7 @@ type university_dept_Departments {
 }
 
 type 
university_dept_Departments__createDepartment__deptHead__gqlv_action_parameter {
+  autoComplete(name: String, search: String): [university_dept_DeptHead]
   disabled(deptHead: university_dept_DeptHead__gqlv_input, name: String): 
String
   hidden(name: String): Boolean
   validity(deptHead: university_dept_DeptHead__gqlv_input): String

Reply via email to