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
