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 7c4cf784887ed368c94015446f992e23952bb0a7 Author: danhaywood <[email protected]> AuthorDate: Tue Jan 30 15:26:28 2024 +0000 CAUSEWAY-3676: mutation support for actions now works --- .../viewer/graphql/model/domain/GqlvMutation.java | 125 +++++++++++++++------ viewers/graphql/test/src/test/resources/schema.gql | 22 ++-- .../viewer/toplevel/GqlvTopLevelMutation.java | 125 +-------------------- 3 files changed, 104 insertions(+), 168 deletions(-) diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutation.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutation.java index d6f5298cef..d07693e89b 100644 --- a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutation.java +++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvMutation.java @@ -18,62 +18,74 @@ */ package org.apache.causeway.viewer.graphql.model.domain; -import graphql.schema.*; +import java.util.ArrayList; -import lombok.extern.log4j.Log4j2; -import lombok.val; +import graphql.schema.DataFetchingEnvironment; +import graphql.schema.GraphQLArgument; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLList; +import graphql.schema.GraphQLOutputType; +import graphql.schema.GraphQLType; + +import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition; + +import org.springframework.lang.Nullable; import org.apache.causeway.applib.annotation.Where; -import org.apache.causeway.applib.services.bookmark.BookmarkService; import org.apache.causeway.commons.collections.Can; import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy; import org.apache.causeway.core.metamodel.facets.actcoll.typeof.TypeOfFacet; import org.apache.causeway.core.metamodel.object.ManagedObject; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; import org.apache.causeway.core.metamodel.spec.feature.ObjectAction; +import org.apache.causeway.core.metamodel.spec.feature.ObjectActionParameter; +import org.apache.causeway.core.metamodel.spec.feature.OneToManyActionParameter; +import org.apache.causeway.core.metamodel.spec.feature.OneToOneActionParameter; import org.apache.causeway.viewer.graphql.applib.types.TypeMapper; import org.apache.causeway.viewer.graphql.model.context.Context; import org.apache.causeway.viewer.graphql.model.exceptions.DisabledException; import org.apache.causeway.viewer.graphql.model.exceptions.HiddenException; -import org.apache.causeway.viewer.graphql.model.mmproviders.ObjectActionProvider; -import org.apache.causeway.viewer.graphql.model.mmproviders.ObjectSpecificationProvider; - -import org.springframework.lang.Nullable; -import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition; +import lombok.val; +import lombok.extern.log4j.Log4j2; @Log4j2 public class GqlvMutation { private final Holder holder; + private final ObjectSpecification objectSpec; + private final ObjectAction objectAction; private final Context context; private final GraphQLFieldDefinition field; private String argumentName; public GqlvMutation( final Holder holder, - final Context context) { + final ObjectSpecification objectSpec, + final ObjectAction objectAction, + final Context context) { this.holder = holder; + this.objectSpec = objectSpec; + this.objectAction = objectAction; this.context = context; this.argumentName = context.causewayConfiguration.getViewer().getGraphql().getMutation().getTargetArgName(); - val objectSpec = holder.getObjectSpecification(); - val objectAction = holder.getObjectAction(); - GraphQLOutputType type = typeFor(objectAction); if (type != null) { val fieldBuilder = newFieldDefinition() .name(fieldName(objectSpec, objectAction)) .type(type); - holder.addGqlArguments(fieldBuilder, TypeMapper.InputContext.INVOKE); + addGqlArguments(fieldBuilder); this.field = holder.addField(fieldBuilder.build()); } else { this.field = null; } } - private static String fieldName(ObjectSpecification objectSpecification, ObjectAction objectAction) { + private static String fieldName( + final ObjectSpecification objectSpecification, + final ObjectAction objectAction) { return TypeNames.objectTypeNameFor(objectSpecification) + "__" + objectAction.getId(); } @@ -117,21 +129,18 @@ public class GqlvMutation { private Object invoke(final DataFetchingEnvironment dataFetchingEnvironment) { - val objectSpecification = holder.getObjectSpecification(); - val objectAction = holder.getObjectAction(); - - val isService = objectSpecification.getBeanSort().isManagedBeanContributing(); + val isService = objectSpec.getBeanSort().isManagedBeanContributing(); Object sourcePojo; if (isService) { - sourcePojo = context.serviceRegistry.lookupServiceElseFail(objectSpecification.getCorrespondingClass()); + sourcePojo = context.serviceRegistry.lookupServiceElseFail(objectSpec.getCorrespondingClass()); } else { Object target = dataFetchingEnvironment.getArgument(argumentName); - sourcePojo = GqlvAction.asPojo(objectSpecification, target, context.bookmarkService) + sourcePojo = GqlvAction.asPojo(objectSpec, target, context.bookmarkService) .orElseThrow(); // TODO: better error handling if no such object found. } - ManagedObject managedObject = ManagedObject.adaptSingular(objectSpecification, sourcePojo); + ManagedObject managedObject = ManagedObject.adaptSingular(objectSpec, sourcePojo); val visibleConsent = objectAction.isVisible(managedObject, InteractionInitiatedBy.USER, Where.ANYWHERE); if (visibleConsent.isVetoed()) { @@ -144,7 +153,7 @@ public class GqlvMutation { } val head = objectAction.interactionHead(managedObject); - val argumentManagedObjects = holder.argumentManagedObjectsFor(dataFetchingEnvironment, objectAction, context.bookmarkService); + val argumentManagedObjects = argumentManagedObjectsFor(dataFetchingEnvironment, objectAction); val validityConsent = objectAction.isArgumentSetValid(head, argumentManagedObjects, InteractionInitiatedBy.USER); if (validityConsent.isVetoed()) { @@ -155,18 +164,64 @@ public class GqlvMutation { return resultManagedObject.getPojo(); } + + // TODO: adapted from GqlvAction - rationalize? + private void addGqlArguments(final GraphQLFieldDefinition.Builder fieldBuilder) { + + val arguments = new ArrayList<GraphQLArgument>(); + val argName = context.causewayConfiguration.getViewer().getGraphql().getMutation().getTargetArgName(); + + // add target (if not a service) + if (! objectSpec.getBeanSort().isManagedBeanContributing()) { + arguments.add( + GraphQLArgument.newArgument() + .name(argName) + .type(context.typeMapper.inputTypeFor(objectSpec)) + .build() + ); + } + + val parameters = objectAction.getParameters(); + parameters.stream() + .map(this::gqlArgumentFor) + .forEach(arguments::add); + + if (!arguments.isEmpty()) { + fieldBuilder.arguments(arguments); + } + } + + // adapted from GqlvAction + GraphQLArgument gqlArgumentFor(final ObjectActionParameter objectActionParameter) { + return objectActionParameter.isPlural() + ? gqlArgumentFor((OneToManyActionParameter) objectActionParameter) + : gqlArgumentFor((OneToOneActionParameter) objectActionParameter); + } + + // adapted from GqlvAction + GraphQLArgument gqlArgumentFor(final OneToOneActionParameter oneToOneActionParameter) { + return GraphQLArgument.newArgument() + .name(oneToOneActionParameter.getId()) + .type(context.typeMapper.inputTypeFor(oneToOneActionParameter, TypeMapper.InputContext.INVOKE)) + .build(); + } + + // adapted from GqlvAction + GraphQLArgument gqlArgumentFor(final OneToManyActionParameter oneToManyActionParameter) { + return GraphQLArgument.newArgument() + .name(oneToManyActionParameter.getId()) + .type(context.typeMapper.inputTypeFor(oneToManyActionParameter)) + .build(); + } + + private Can<ManagedObject> argumentManagedObjectsFor( + final DataFetchingEnvironment dataFetchingEnvironment, + final ObjectAction objectAction) { + return GqlvAction.argumentManagedObjectsFor(dataFetchingEnvironment, objectAction, context); + } + public interface Holder - extends GqlvHolder, - ObjectSpecificationProvider, - ObjectActionProvider { - - void addGqlArguments( - final GraphQLFieldDefinition.Builder fieldBuilder, - final TypeMapper.InputContext inputContext); - - Can<ManagedObject> argumentManagedObjectsFor( - final DataFetchingEnvironment dataFetchingEnvironment, - final ObjectAction objectAction, - final BookmarkService bookmarkService); + extends GqlvHolder { } + } diff --git a/viewers/graphql/test/src/test/resources/schema.gql b/viewers/graphql/test/src/test/resources/schema.gql index 94ac2ae9d2..5f6c0fe828 100644 --- a/viewers/graphql/test/src/test/resources/schema.gql +++ b/viewers/graphql/test/src/test/resources/schema.gql @@ -23,22 +23,22 @@ directive @specifiedBy( ) on SCALAR type Mutation { - causeway_applib_PropertyNode__streamChildNodes(target: causeway_applib_PropertyNode__gqlv_input): java_util_stream_Stream - causeway_applib_node_ActionNode__streamChildNodes(target: causeway_applib_node_ActionNode__gqlv_input): java_util_stream_Stream - causeway_applib_node_CollectionNode__streamChildNodes(target: causeway_applib_node_CollectionNode__gqlv_input): java_util_stream_Stream - org_apache_causeway_core_metamodel_inspect_model_MemberNode__streamChildNodes(target: org_apache_causeway_core_metamodel_inspect_model_MemberNode__gqlv_input): java_util_stream_Stream + causeway_applib_PropertyNode__streamChildNodes(_gqlv_target: causeway_applib_PropertyNode__gqlv_input): java_util_stream_Stream + causeway_applib_node_ActionNode__streamChildNodes(_gqlv_target: causeway_applib_node_ActionNode__gqlv_input): java_util_stream_Stream + causeway_applib_node_CollectionNode__streamChildNodes(_gqlv_target: causeway_applib_node_CollectionNode__gqlv_input): java_util_stream_Stream + org_apache_causeway_core_metamodel_inspect_model_MemberNode__streamChildNodes(_gqlv_target: org_apache_causeway_core_metamodel_inspect_model_MemberNode__gqlv_input): java_util_stream_Stream university_admin_AdminMenu__actionWithDisabledParam(firstParam: String!, secondParam: String!, thirdParameter: String!): String university_admin_AdminMenu__actionWithHiddenParam(firstParam: String!, secondParam: String!): String university_admin_AdminMenu__adminAction: String university_admin_AdminMenu__otherAdminAction: String - university_dept_Department__addStaffMember(staffMember: university_dept_StaffMember__gqlv_input!, target: university_dept_Department__gqlv_input): university_dept_Department - university_dept_Department__addStaffMembers(staffMembers: [university_dept_StaffMember__gqlv_input], target: university_dept_Department__gqlv_input): university_dept_Department - university_dept_Department__changeDeptHead(newDeptHead: university_dept_DeptHead__gqlv_input!, target: university_dept_Department__gqlv_input): university_dept_Department - university_dept_Department__changeName(newName: String!, target: university_dept_Department__gqlv_input): university_dept_Department - university_dept_Department__removeStaffMember(staffMember: university_dept_StaffMember__gqlv_input!, target: university_dept_Department__gqlv_input): university_dept_Department + university_dept_Department__addStaffMember(_gqlv_target: university_dept_Department__gqlv_input, staffMember: university_dept_StaffMember__gqlv_input!): university_dept_Department + university_dept_Department__addStaffMembers(_gqlv_target: university_dept_Department__gqlv_input, staffMembers: [university_dept_StaffMember__gqlv_input]): university_dept_Department + university_dept_Department__changeDeptHead(_gqlv_target: university_dept_Department__gqlv_input, newDeptHead: university_dept_DeptHead__gqlv_input!): university_dept_Department + university_dept_Department__changeName(_gqlv_target: university_dept_Department__gqlv_input, newName: String!): university_dept_Department + university_dept_Department__removeStaffMember(_gqlv_target: university_dept_Department__gqlv_input, staffMember: university_dept_StaffMember__gqlv_input!): university_dept_Department university_dept_Departments__createDepartment(deptHead: university_dept_DeptHead__gqlv_input, name: String!): university_dept_Department - university_dept_DeptHead__changeDepartment(department: university_dept_Department__gqlv_input!, target: university_dept_DeptHead__gqlv_input): university_dept_DeptHead - university_dept_DeptHead__changeName(newName: String!, target: university_dept_DeptHead__gqlv_input): university_dept_DeptHead + university_dept_DeptHead__changeDepartment(_gqlv_target: university_dept_DeptHead__gqlv_input, department: university_dept_Department__gqlv_input!): university_dept_DeptHead + university_dept_DeptHead__changeName(_gqlv_target: university_dept_DeptHead__gqlv_input, newName: String!): university_dept_DeptHead university_dept_Staff__createStaffMember(department: university_dept_Department__gqlv_input!, name: String!): university_dept_StaffMember } diff --git a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelMutation.java b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelMutation.java index 00db413aee..3d4a792f53 100644 --- a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelMutation.java +++ b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelMutation.java @@ -3,32 +3,20 @@ package org.apache.causeway.viewer.graphql.viewer.toplevel; import java.util.ArrayList; import java.util.List; -import graphql.schema.DataFetchingEnvironment; import graphql.schema.FieldCoordinates; -import graphql.schema.GraphQLArgument; import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLObjectType; import static graphql.schema.GraphQLObjectType.newObject; -import org.apache.causeway.applib.services.bookmark.BookmarkService; -import org.apache.causeway.commons.collections.Can; -import org.apache.causeway.core.metamodel.object.ManagedObject; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; import org.apache.causeway.core.metamodel.spec.feature.ObjectAction; -import org.apache.causeway.core.metamodel.spec.feature.ObjectActionParameter; -import org.apache.causeway.core.metamodel.spec.feature.OneToManyActionParameter; -import org.apache.causeway.core.metamodel.spec.feature.OneToOneActionParameter; -import org.apache.causeway.viewer.graphql.applib.types.TypeMapper; import org.apache.causeway.viewer.graphql.model.context.Context; -import org.apache.causeway.viewer.graphql.model.domain.GqlvAction; import org.apache.causeway.viewer.graphql.model.domain.GqlvMutation; -import org.apache.causeway.viewer.graphql.model.domain.GqlvHolder; import lombok.Getter; -import lombok.val; -public class GqlvTopLevelMutation implements GqlvHolder { +public class GqlvTopLevelMutation implements GqlvMutation.Holder { private final Context context; @@ -68,25 +56,9 @@ public class GqlvTopLevelMutation implements GqlvHolder { return gqlObjectType; } -// public void addFieldFor( -// final GqlvDomainService domainService, -// final GraphQLCodeRegistry.Builder codeRegistryBuilder) { -// -// GraphQLFieldDefinition topLevelQueryField = domainService.createTopLevelQueryField(); -// gqlObjectBuilder.field(topLevelQueryField); -// -// codeRegistryBuilder.dataFetcher( -// // TODO: it would be nice to make these typesafe... -// FieldCoordinates.coordinates("Mutation", topLevelQueryField.getName()), -// (DataFetcher<Object>) environment -> domainService.getServicePojo()); -// -// } - public void addAction(ObjectSpecification objectSpec, final ObjectAction objectAction) { - // TODO: kinda ugly the responsibilities here - val holder = new GqlvMutationHolder(this, objectSpec, objectAction, context); - actions.add(new GqlvMutation(holder, context)); + actions.add(new GqlvMutation(this, objectSpec, objectAction, context)); } @Override @@ -103,98 +75,7 @@ public class GqlvTopLevelMutation implements GqlvHolder { public void addDataFetchers() { actions.forEach(GqlvMutation::addDataFetcher); } -} - -class GqlvMutationHolder implements GqlvMutation.Holder { - - private final GqlvTopLevelMutation gqlvTopLevelMutation; - private final ObjectSpecification objectSpec; - private final ObjectAction objectAction; - private final Context context; - - public GqlvMutationHolder( - final GqlvTopLevelMutation gqlvTopLevelMutation, - final ObjectSpecification objectSpec, - final ObjectAction objectAction, - final Context context) { - this.objectSpec = objectSpec; - this.objectAction = objectAction; - this.gqlvTopLevelMutation = gqlvTopLevelMutation; - this.context = context; - } - - @Override public ObjectAction getObjectAction() {return objectAction;} - @Override public ObjectAction getObjectMember() {return objectAction;} - @Override public ObjectSpecification getObjectSpecification() {return objectSpec;} - - // TODO: adapted from GqlvAction - @Override - public void addGqlArguments( - final GraphQLFieldDefinition.Builder fieldBuilder, - final TypeMapper.InputContext inputContext) { - - val arguments = new ArrayList<GraphQLArgument>(); - val argName = context.causewayConfiguration.getViewer().getGraphql().getMutation().getTargetArgName(); - - // add target (if not a service) - if (! objectSpec.getBeanSort().isManagedBeanContributing()) { - arguments.add( - GraphQLArgument.newArgument() - .name(argName) - .type(context.typeMapper.inputTypeFor(objectSpec)) - .build() - ); - } - - val parameters = objectAction.getParameters(); - parameters.stream() - .map(this::gqlArgumentFor) - .forEach(arguments::add); - - if (!arguments.isEmpty()) { - fieldBuilder.arguments(arguments); - } - } - // adapted from GqlvAction - GraphQLArgument gqlArgumentFor(final ObjectActionParameter objectActionParameter) { - return objectActionParameter.isPlural() - ? gqlArgumentFor((OneToManyActionParameter) objectActionParameter) - : gqlArgumentFor((OneToOneActionParameter) objectActionParameter); - } - - // adapted from GqlvAction - GraphQLArgument gqlArgumentFor(final OneToOneActionParameter oneToOneActionParameter) { - return GraphQLArgument.newArgument() - .name(oneToOneActionParameter.getId()) - .type(context.typeMapper.inputTypeFor(oneToOneActionParameter, TypeMapper.InputContext.INVOKE)) - .build(); - } - - // TODO: copied from GqlvAction - GraphQLArgument gqlArgumentFor(final OneToManyActionParameter oneToManyActionParameter) { - return GraphQLArgument.newArgument() - .name(oneToManyActionParameter.getId()) - .type(context.typeMapper.inputTypeFor(oneToManyActionParameter)) - .build(); - } - - @Override - public Can<ManagedObject> argumentManagedObjectsFor( - final DataFetchingEnvironment dataFetchingEnvironment, - final ObjectAction objectAction, - final BookmarkService bookmarkService) { - return GqlvAction.argumentManagedObjectsFor(dataFetchingEnvironment, objectAction, context); - } - - @Override - public GraphQLFieldDefinition addField(GraphQLFieldDefinition fieldDefinition) { - return gqlvTopLevelMutation.addField(fieldDefinition); - } - - @Override - public FieldCoordinates coordinatesFor(GraphQLFieldDefinition fieldDefinition) { - return gqlvTopLevelMutation.coordinatesFor(fieldDefinition); - } } +
