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);
     }
 
 }

Reply via email to