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 a0b2d0767ece32caf5cdb0bb7a8e40bc52ef9bb3 Author: danhaywood <[email protected]> AuthorDate: Tue Jan 30 08:36:39 2024 +0000 CAUSEWAY-3676: sketching out introducing mutations (3) --- .../viewer/graphql/applib/types/TypeMapper.java | 2 + .../viewer/graphql/model/domain/GqlvAction.java | 35 ++-- .../model/domain/GqlvActionInvokeMutating.java | 175 ++++++++++++++++++++ .../graphql/model/types/TypeMapperDefault.java | 13 +- .../integration/GraphQlSourceForCauseway.java | 30 ++-- .../viewer/toplevel/GqlvTopLevelMutation.java | 180 +++++++++++++++++---- 6 files changed, 367 insertions(+), 68 deletions(-) diff --git a/viewers/graphql/applib/src/main/java/org/apache/causeway/viewer/graphql/applib/types/TypeMapper.java b/viewers/graphql/applib/src/main/java/org/apache/causeway/viewer/graphql/applib/types/TypeMapper.java index ccea137f8e..7cd932cd6e 100644 --- a/viewers/graphql/applib/src/main/java/org/apache/causeway/viewer/graphql/applib/types/TypeMapper.java +++ b/viewers/graphql/applib/src/main/java/org/apache/causeway/viewer/graphql/applib/types/TypeMapper.java @@ -66,6 +66,8 @@ public interface TypeMapper { GraphQLList inputTypeFor(final OneToManyActionParameter oneToManyActionParameter, final InputContext inputContextUnused); + GraphQLInputType inputTypeFor(final ObjectSpecification elementType); + Object adaptPojo( final Object argumentValue, final ObjectSpecification elementType); 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 bc784914d3..42b026c4cc 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 @@ -97,6 +97,13 @@ public class GqlvAction final ObjectAction objectAction, final BookmarkService bookmarkService) { + return argumentManagedObjectsFor(dataFetchingEnvironment, objectAction, context); + } + + public static Can<ManagedObject> argumentManagedObjectsFor( + final DataFetchingEnvironment dataFetchingEnvironment, + final ObjectAction objectAction, + final Context context) { Map<String, Object> argumentPojos = dataFetchingEnvironment.getArguments(); Can<ObjectActionParameter> parameters = objectAction.getParameters(); return parameters @@ -106,7 +113,7 @@ public class GqlvAction switch (elementType.getBeanSort()) { case VALUE: - return adaptValue(oap, argumentValue); + return adaptValue(oap, argumentValue, context); case ENTITY: case VIEW_MODEL: @@ -117,12 +124,12 @@ public class GqlvAction if (argumentValue instanceof List) { val argumentValueList = (List<Object>) argumentValue; pojoOrPojoList = argumentValueList.stream() - .map(value -> asPojo(oap.getElementType(), value, bookmarkService)) + .map(value -> asPojo(oap.getElementType(), value, context.bookmarkService)) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList()); } else { - pojoOrPojoList = asPojo(oap.getElementType(), argumentValue, bookmarkService).orElse(null); + pojoOrPojoList = asPojo(oap.getElementType(), argumentValue, context.bookmarkService).orElse(null); } return ManagedObject.adaptParameter(oap, pojoOrPojoList); @@ -140,16 +147,17 @@ public class GqlvAction }); } - private ManagedObject adaptValue( + private static ManagedObject adaptValue( final ObjectActionParameter oap, - final Object argumentValue) { + final Object argumentValue, + final Context context1) { val elementType = oap.getElementType(); if (argumentValue == null) { return ManagedObject.empty(elementType); } - val argPojo = context.typeMapper.adaptPojo(argumentValue, elementType); + val argPojo = context1.typeMapper.adaptPojo(argumentValue, elementType); return ManagedObject.adaptParameter(oap, argPojo); } @@ -185,21 +193,6 @@ public class GqlvAction } } - public void addGqlArgument( - final ObjectAction objectAction, - final GraphQLFieldDefinition.Builder builder, - final TypeMapper.InputContext inputContext, - final int paramNum) { - - val parameters = objectAction.getParameters(); - val arguments = parameters.get(paramNum).stream() - .map(objectActionParameter -> gqlArgumentFor(objectActionParameter, inputContext)) - .collect(Collectors.toList()); - if (!arguments.isEmpty()) { - builder.arguments(arguments); - } - } - GraphQLArgument gqlArgumentFor( final ObjectActionParameter objectActionParameter, final TypeMapper.InputContext inputContext) { diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionInvokeMutating.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionInvokeMutating.java new file mode 100644 index 0000000000..bac4dc7b77 --- /dev/null +++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvActionInvokeMutating.java @@ -0,0 +1,175 @@ +/* + * 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 graphql.schema.*; + +import lombok.extern.log4j.Log4j2; +import lombok.val; + +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.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.fetcher.BookmarkedPojo; +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; + +@Log4j2 +public class GqlvActionInvokeMutating { + + private final Holder holder; + private final Context context; + private final GraphQLFieldDefinition field; + + public GqlvActionInvokeMutating( + final Holder holder, + final Context context) { + this.holder = holder; + this.context = context; + + val objectAction = holder.getObjectAction(); + + GraphQLOutputType type = typeFor(objectAction); + if (type != null) { + val fieldBuilder = newFieldDefinition() + .name(fieldNameForSemanticsOf(objectAction)) + .type(type); + holder.addGqlArguments(fieldBuilder, TypeMapper.InputContext.INVOKE); + this.field = holder.addField(fieldBuilder.build()); + } else { + this.field = null; + } + } + + private static String fieldNameForSemanticsOf(ObjectAction objectAction) { + switch (objectAction.getSemantics()) { + case SAFE_AND_REQUEST_CACHEABLE: + case SAFE: + return "invoke"; + case IDEMPOTENT: + case IDEMPOTENT_ARE_YOU_SURE: + return "invokeIdempotent"; + case NON_IDEMPOTENT: + case NON_IDEMPOTENT_ARE_YOU_SURE: + case NOT_SPECIFIED: + default: + return "invokeNonIdempotent"; + } + } + + @Nullable + private GraphQLOutputType typeFor(final ObjectAction objectAction){ + ObjectSpecification objectSpecification = objectAction.getReturnType(); + switch (objectSpecification.getBeanSort()){ + + case COLLECTION: + + TypeOfFacet facet = objectAction.getFacet(TypeOfFacet.class); + if (facet == null) { + log.warn("Unable to locate TypeOfFacet for {}", objectAction.getFeatureIdentifier().getFullIdentityString()); + return null; + } + val objectSpecificationOfCollectionElement = facet.elementSpec(); + GraphQLType wrappedType = context.typeMapper.outputTypeFor(objectSpecificationOfCollectionElement); + if (wrappedType == null) { + log.warn("Unable to create wrapped type of for {} for action {}", + objectSpecificationOfCollectionElement.getFullIdentifier(), + objectAction.getFeatureIdentifier().getFullIdentityString()); + return null; + } + return GraphQLList.list(wrappedType); + + case VALUE: + case ENTITY: + case VIEW_MODEL: + default: + return context.typeMapper.outputTypeFor(objectSpecification); + + } + } + + public void addDataFetcher() { + context.codeRegistryBuilder.dataFetcher( + holder.coordinatesFor(field), + this::invoke + ); + } + + private Object invoke(final DataFetchingEnvironment dataFetchingEnvironment) { + + val sourcePojo = BookmarkedPojo.sourceFrom(dataFetchingEnvironment); + + val objectSpecification = context.specificationLoader.loadSpecification(sourcePojo.getClass()); + if (objectSpecification == null) { + return null; + } + + val objectAction = holder.getObjectAction(); + val managedObject = ManagedObject.adaptSingular(objectSpecification, sourcePojo); + + val visibleConsent = objectAction.isVisible(managedObject, InteractionInitiatedBy.USER, Where.ANYWHERE); + if (visibleConsent.isVetoed()) { + throw new HiddenException(objectAction.getFeatureIdentifier()); + } + + val usableConsent = objectAction.isUsable(managedObject, InteractionInitiatedBy.USER, Where.ANYWHERE); + if (usableConsent.isVetoed()) { + throw new DisabledException(objectAction.getFeatureIdentifier()); + } + + val head = objectAction.interactionHead(managedObject); + val argumentManagedObjects = holder.argumentManagedObjectsFor(dataFetchingEnvironment, objectAction, context.bookmarkService); + + val validityConsent = objectAction.isArgumentSetValid(head, argumentManagedObjects, InteractionInitiatedBy.USER); + if (validityConsent.isVetoed()) { + throw new IllegalArgumentException(validityConsent.getReasonAsString().orElse("Invalid")); + } + + val resultManagedObject = objectAction.execute(head, argumentManagedObjects, InteractionInitiatedBy.USER); + return resultManagedObject.getPojo(); + } + + public interface Holder + extends GqlvHolder, + ObjectSpecificationProvider, + ObjectActionProvider { + + void addGqlArguments( + final GraphQLFieldDefinition.Builder fieldBuilder, + final TypeMapper.InputContext inputContext); + + Can<ManagedObject> argumentManagedObjectsFor( + DataFetchingEnvironment dataFetchingEnvironment, + ObjectAction objectAction, + BookmarkService bookmarkService); + } +} diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/types/TypeMapperDefault.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/types/TypeMapperDefault.java index 7251e3713a..c102b1cb51 100644 --- a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/types/TypeMapperDefault.java +++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/types/TypeMapperDefault.java @@ -100,10 +100,12 @@ public class TypeMapperDefault implements TypeMapper { pair(Boolean.class, Scalars.GraphQLBoolean) ); + @Override public GraphQLScalarType scalarTypeFor(final Class<?> c){ return SCALAR_BY_CLASS.getOrDefault(c, Scalars.GraphQLString); } + @Override public GraphQLOutputType outputTypeFor(final OneToOneFeature oneToOneFeature) { ObjectSpecification otoaObjectSpec = oneToOneFeature.getElementType(); switch (otoaObjectSpec.getBeanSort()) { @@ -127,6 +129,7 @@ public class TypeMapperDefault implements TypeMapper { return null; } + @Override @Nullable public GraphQLOutputType outputTypeFor(final ObjectSpecification objectSpecification){ @@ -149,11 +152,13 @@ public class TypeMapperDefault implements TypeMapper { } } + @Override @Nullable public GraphQLList listTypeForElementTypeOf(OneToManyAssociation oneToManyAssociation) { ObjectSpecification elementType = oneToManyAssociation.getElementType(); return listTypeFor(elementType); } + @Override @Nullable public GraphQLList listTypeFor(ObjectSpecification elementType) { switch (elementType.getBeanSort()) { case VIEW_MODEL: @@ -165,6 +170,7 @@ public class TypeMapperDefault implements TypeMapper { return null; } + @Override public GraphQLInputType inputTypeFor( final OneToOneFeature oneToOneFeature, final InputContext inputContext) { @@ -192,12 +198,14 @@ public class TypeMapperDefault implements TypeMapper { } } + @Override public GraphQLList inputTypeFor(final OneToManyActionParameter oneToManyActionParameter, final InputContext inputContextUnused){ ObjectSpecification elementType = oneToManyActionParameter.getElementType(); - return GraphQLList.list(inputTypeFor_(elementType)); + return GraphQLList.list(inputTypeFor(elementType)); } - private GraphQLInputType inputTypeFor_(final ObjectSpecification elementType){ + @Override + public GraphQLInputType inputTypeFor(final ObjectSpecification elementType){ switch (elementType.getBeanSort()) { case ABSTRACT: case ENTITY: @@ -216,6 +224,7 @@ public class TypeMapperDefault implements TypeMapper { } } + @Override public Object adaptPojo( final Object argumentValue, final ObjectSpecification elementType) { diff --git a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/integration/GraphQlSourceForCauseway.java b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/integration/GraphQlSourceForCauseway.java index 84350f0d16..62a00b5273 100644 --- a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/integration/GraphQlSourceForCauseway.java +++ b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/integration/GraphQlSourceForCauseway.java @@ -23,6 +23,7 @@ import java.util.Comparator; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.causeway.core.metamodel.spec.feature.MixedIn; import org.apache.causeway.viewer.graphql.viewer.toplevel.GqlvTopLevelMutation; import org.springframework.graphql.execution.GraphQlSource; @@ -102,14 +103,16 @@ public class GraphQlSourceForCauseway implements GraphQlSource { } val codeRegistryBuilder = GraphQLCodeRegistry.newCodeRegistry(); + val context = new Context(codeRegistryBuilder, bookmarkService, specificationLoader, typeMapper, causewayConfiguration, causewaySystemEnvironment); // add to the top-level query type and (dependent on configuration) the top-level mutation type also val topLevelQuery = new GqlvTopLevelQuery(serviceRegistry, codeRegistryBuilder); val topLevelMutation = causewayConfiguration.getViewer().getGraphql().getApiVariant() == CausewayConfiguration.Viewer.Graphql.ApiVariant.QUERY_AND_MUTATIONS ? - new GqlvTopLevelMutation(serviceRegistry, codeRegistryBuilder) + new GqlvTopLevelMutation(context) : null; + val objectSpecifications = specificationLoader.snapshotSpecifications() .distinct((a, b) -> a.getLogicalTypeName().equals(b.getLogicalTypeName())) .filter(x -> x.isEntityOrViewModelOrAbstract() || x.getBeanSort().isManagedBeanContributing()) @@ -117,8 +120,6 @@ public class GraphQlSourceForCauseway implements GraphQlSource { .toList(); // add to top-level query - val context = new Context(codeRegistryBuilder, bookmarkService, specificationLoader, typeMapper, causewayConfiguration, causewaySystemEnvironment); - objectSpecifications.forEach(objectSpec -> { switch (objectSpec.getBeanSort()) { case MANAGED_BEAN_CONTRIBUTING: // @DomainService @@ -129,6 +130,19 @@ public class GraphQlSourceForCauseway implements GraphQlSource { }); topLevelQuery.buildQueryType(); + // add top-level mutation (if application configuration requires it) + if (topLevelMutation != null) { + objectSpecifications.forEach(objectSpec -> { + objectSpec.streamActions(context.getActionScope(), MixedIn.INCLUDED) + .filter(x -> ! x.getSemantics().isSafeInNature()) + .forEach(objectAction -> topLevelMutation.addAction(objectSpec, objectAction)); + + }); + topLevelMutation.buildMutationType(); + topLevelMutation.addFetchers(); + } + + // add remaining domain objects objectSpecifications.forEach(objectSpec -> { switch (objectSpec.getBeanSort()) { @@ -141,17 +155,9 @@ public class GraphQlSourceForCauseway implements GraphQlSource { gqlvDomainObject.addDataFetchers(); break; - } }); - if (topLevelMutation != null) { - objectSpecifications.forEach(objectSpec -> { - - }); - topLevelMutation.buildMutationType(); - topLevelMutation.addFetchers(); - } // finalize the fetcher/mutator code that's been registered @@ -163,7 +169,7 @@ public class GraphQlSourceForCauseway implements GraphQlSource { .additionalTypes(graphQLTypeRegistry.getGraphQLTypes()) .codeRegistry(codeRegistry); if (topLevelMutation != null) { - schemaBuilder.mutation(topLevelMutation.getObjectType()); + schemaBuilder.mutation(topLevelMutation.getGqlObjectType()); } return schemaBuilder .build(); 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 c04fa400b2..9442da2755 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 @@ -1,68 +1,73 @@ package org.apache.causeway.viewer.graphql.viewer.toplevel; -import graphql.Scalars; -import graphql.schema.DataFetcher; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import graphql.schema.DataFetchingEnvironment; import graphql.schema.FieldCoordinates; -import graphql.schema.GraphQLCodeRegistry; +import graphql.schema.GraphQLArgument; import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLInputType; import graphql.schema.GraphQLObjectType; -import static graphql.schema.FieldCoordinates.coordinates; -import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition; import static graphql.schema.GraphQLObjectType.newObject; -import org.apache.causeway.applib.services.registry.ServiceRegistry; -import org.apache.causeway.viewer.graphql.model.domain.GqlvDomainService; +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.GqlvActionInvokeMutating; +import org.apache.causeway.viewer.graphql.model.domain.GqlvHolder; import lombok.Getter; +import lombok.val; -public class GqlvTopLevelMutation { +public class GqlvTopLevelMutation implements GqlvHolder { - private final ServiceRegistry serviceRegistry; - private final GraphQLCodeRegistry.Builder codeRegistryBuilder; + private final Context context; - @Getter final GraphQLObjectType.Builder gqlObjectBuilder; + @Getter final GraphQLObjectType.Builder gqlObjectTypeBuilder; - @Getter private GraphQLFieldDefinition numServicesField; /** * Built using {@link #buildMutationType()} */ - private GraphQLObjectType objectType; + private GraphQLObjectType gqlObjectType; + private final Map<String, GqlvActionInvokeMutating> actions = new LinkedHashMap<String, GqlvActionInvokeMutating>(); - public GqlvTopLevelMutation( - final ServiceRegistry serviceRegistry, - final GraphQLCodeRegistry.Builder codeRegistryBuilder) { - this.serviceRegistry = serviceRegistry; - this.codeRegistryBuilder = codeRegistryBuilder; - gqlObjectBuilder = newObject().name("Mutation"); + public GqlvTopLevelMutation(final Context context) { + this.context = context; + gqlObjectTypeBuilder = newObject().name("Mutation"); - numServicesField = newFieldDefinition() - .name("numServices") - .type(Scalars.GraphQLInt) - .build(); - gqlObjectBuilder.field(numServicesField); } public GraphQLObjectType buildMutationType() { - if (objectType != null) { + if (gqlObjectType != null) { throw new IllegalStateException("Mutation type has already been built"); } - return objectType = gqlObjectBuilder.build(); + return gqlObjectType = gqlObjectTypeBuilder.build(); } /** * * @see #buildMutationType() */ - public GraphQLObjectType getObjectType() { - if (objectType == null) { + public GraphQLObjectType getGqlObjectType() { + if (gqlObjectType == null) { throw new IllegalStateException("Mutation type has not yet been built"); } - return objectType; + return gqlObjectType; } // public void addFieldFor( @@ -79,11 +84,120 @@ public class GqlvTopLevelMutation { // // } + + public void addAction(ObjectSpecification objectSpec, final ObjectAction objectAction) { + // TODO: kinda ugly the responsibilities here + val holder = new GqlvActionInvokeMutatingHolder(this, objectSpec, objectAction, context); + actions.put(objectAction.getId(), new GqlvActionInvokeMutating(holder, context)); + } + + @Override + public GraphQLFieldDefinition addField(GraphQLFieldDefinition field) { + gqlObjectTypeBuilder.field(field); + return field; + } + + @Override + public FieldCoordinates coordinatesFor(GraphQLFieldDefinition fieldDefinition) { + return FieldCoordinates.coordinates(gqlObjectType, fieldDefinition); + } + public void addFetchers() { - codeRegistryBuilder - .dataFetcher( - coordinates(getObjectType(), getNumServicesField()), - (DataFetcher<Object>) environment -> this.serviceRegistry.streamRegisteredBeans().count()); + + } +} + +class GqlvActionInvokeMutatingHolder implements GqlvActionInvokeMutating.Holder { + + private final GqlvTopLevelMutation gqlvTopLevelMutation; + private final ObjectSpecification objectSpec; + private final ObjectAction objectAction; + private final Context context; + + public GqlvActionInvokeMutatingHolder( + 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) { + + // add target (if not a service) + if (!objectSpec.getBeanSort().isManagedBeanContributing()) { + GraphQLInputType graphQLInputType = context.typeMapper.inputTypeFor(objectSpec); + fieldBuilder.argument(GraphQLArgument.newArgument() + .name("target") + .type(graphQLInputType) + .build()); + } + + val parameters = objectAction.getParameters(); + val arguments = parameters.stream() + .map(objectActionParameter -> gqlArgumentFor(objectActionParameter, inputContext)) + .collect(Collectors.toList()); + if (!arguments.isEmpty()) { + fieldBuilder.arguments(arguments); + } + } + + // TODO: copied from GqlvAction + GraphQLArgument gqlArgumentFor( + final ObjectActionParameter objectActionParameter, + final TypeMapper.InputContext inputContext) { + return objectActionParameter.isPlural() + ? gqlArgumentFor((OneToManyActionParameter) objectActionParameter, inputContext) + : gqlArgumentFor((OneToOneActionParameter) objectActionParameter, inputContext); + } + + // TODO: copied from GqlvAction + GraphQLArgument gqlArgumentFor( + final OneToOneActionParameter oneToOneActionParameter, + final TypeMapper.InputContext inputContext) { + return GraphQLArgument.newArgument() + .name(oneToOneActionParameter.getId()) + .type(context.typeMapper.inputTypeFor(oneToOneActionParameter, inputContext)) + .build(); + } + + // TODO: copied from GqlvAction + GraphQLArgument gqlArgumentFor( + final OneToManyActionParameter oneToManyActionParameter, + final TypeMapper.InputContext inputContext) { + return GraphQLArgument.newArgument() + .name(oneToManyActionParameter.getId()) + .type(context.typeMapper.inputTypeFor(oneToManyActionParameter, inputContext)) + .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); } }
