You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2024/01/30 16:46:14 UTC
(causeway) 01/08: CAUSEWAY-3676: sketching out introducing mutations
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 92127e50a5584abd307ae5f94dc686857b8e5730
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Jan 30 06:41:55 2024 +0000
CAUSEWAY-3676: sketching out introducing mutations
---
.../graphql/model/domain/GqlvDomainObject.java | 7 +-
viewers/graphql/test/src/test/resources/schema.gql | 1 -
.../integration/GraphQlSourceForCauseway.java | 123 +++++++++------------
...opLevelQuery.java => GqlvTopLevelMutation.java} | 70 ++++++------
.../graphql/viewer/toplevel/GqlvTopLevelQuery.java | 22 ++--
5 files changed, 101 insertions(+), 122 deletions(-)
diff --git a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvDomainObject.java b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvDomainObject.java
index a4349b4434..1416c7326c 100644
--- a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvDomainObject.java
+++ b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/GqlvDomainObject.java
@@ -89,14 +89,13 @@ public class GqlvDomainObject implements GqlvAction.Holder, GqlvProperty.Holder,
gqlInputObjectType = inputTypeBuilder.build();
addMembers();
+ }
- // register types
+ public void addTypesInto(GraphQLTypeRegistry graphQLTypeRegistry) {
gqlObjectType = gqlObjectTypeBuilder.build();
graphQLTypeRegistry.addTypeIfNotAlreadyPresent(gqlObjectType);
meta.registerTypesInto(graphQLTypeRegistry);
graphQLTypeRegistry.addTypeIfNotAlreadyPresent(gqlInputObjectType);
-
- addDataFetchers();
}
@@ -141,7 +140,7 @@ public class GqlvDomainObject implements GqlvAction.Holder, GqlvProperty.Holder,
}
- private void addDataFetchers() {
+ public void addDataFetchers() {
meta.addDataFetchers();
properties.forEach((id, property) -> property.addDataFetcher());
collections.forEach((id, collection) -> collection.addDataFetcher());
diff --git a/viewers/graphql/test/src/test/resources/schema.gql b/viewers/graphql/test/src/test/resources/schema.gql
index f59319134e..363755e40d 100644
--- a/viewers/graphql/test/src/test/resources/schema.gql
+++ b/viewers/graphql/test/src/test/resources/schema.gql
@@ -26,7 +26,6 @@ type Query {
causeway_applib_UserMenu: causeway_applib_UserMenu
causeway_conf_ConfigurationMenu: causeway_conf_ConfigurationMenu
causeway_security_LogoutMenu: causeway_security_LogoutMenu
- numServices: Int
university_admin_AdminMenu: university_admin_AdminMenu
university_calc_Calculator: university_calc_Calculator
university_dept_Departments: university_dept_Departments
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 e0403c37e3..6ae877b43b 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
@@ -19,12 +19,12 @@
package org.apache.causeway.viewer.graphql.viewer.integration;
import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
+import org.apache.causeway.viewer.graphql.viewer.toplevel.GqlvTopLevelMutation;
+
import org.springframework.graphql.execution.GraphQlSource;
import org.springframework.stereotype.Service;
@@ -35,12 +35,10 @@ import org.apache.causeway.core.config.CausewayConfiguration;
import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
import org.apache.causeway.core.config.metamodel.specloader.IntrospectionMode;
import org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
-import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
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.GqlvDomainObject;
-import org.apache.causeway.viewer.graphql.model.domain.GqlvDomainService;
import org.apache.causeway.viewer.graphql.model.registry.GraphQLTypeRegistry;
import org.apache.causeway.viewer.graphql.viewer.toplevel.GqlvTopLevelQuery;
@@ -53,7 +51,6 @@ import graphql.execution.DataFetcherExceptionHandlerParameters;
import graphql.execution.DataFetcherExceptionHandlerResult;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLSchema;
-import graphql.schema.GraphQLType;
@Service()
@RequiredArgsConstructor(onConstructor_ = {@Inject})
@@ -83,7 +80,6 @@ public class GraphQlSourceForCauseway implements GraphQlSource {
public GraphQL graphQl() {
if (graphQL == null) {
graphQL = GraphQL.newGraphQL(schema())
-// .instrumentation(new TracingInstrumentation())
.defaultDataFetcherExceptionHandler(new DataFetcherExceptionHandler() {
@Override
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
@@ -91,6 +87,7 @@ public class GraphQlSourceForCauseway implements GraphQlSource {
}
})
.queryExecutionStrategy(executionStrategy)
+ .mutationExecutionStrategy(executionStrategy)
.build();
}
return graphQL;
@@ -104,94 +101,78 @@ public class GraphQlSourceForCauseway implements GraphQlSource {
throw new IllegalStateException("Metamodel is not fully introspected");
}
- final GraphQLCodeRegistry.Builder codeRegistryBuilder = GraphQLCodeRegistry.newCodeRegistry();
+ val codeRegistryBuilder = GraphQLCodeRegistry.newCodeRegistry();
- // add to the top-level query
- // (and also add behaviour to the child types)
+ // 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)
+ : null;
- List<ObjectSpecification> objectSpecifications = specificationLoader.snapshotSpecifications()
+ val objectSpecifications = specificationLoader.snapshotSpecifications()
.distinct((a, b) -> a.getLogicalTypeName().equals(b.getLogicalTypeName()))
.filter(x -> x.isEntityOrViewModelOrAbstract() || x.getBeanSort().isManagedBeanContributing())
.sorted(Comparator.comparing(HasLogicalType::getLogicalTypeName))
.toList();
- objectSpecifications.forEach(objectSpec -> addToSchema(objectSpec, topLevelQuery, codeRegistryBuilder));
-
- topLevelQuery.buildQueryType();
-
-
- topLevelQuery.addFetchers();
-
- // finalize the fetcher/mutator code that's been registered
- val codeRegistry = codeRegistryBuilder.build();
+ // add to top-level query
+ val context = new Context(codeRegistryBuilder, bookmarkService, specificationLoader, typeMapper, causewayConfiguration, causewaySystemEnvironment);
- // build the schema
- Set<GraphQLType> graphQLTypes = graphQLTypeRegistry.getGraphQLTypes();
- return GraphQLSchema.newSchema()
- .query(topLevelQuery.getQueryType())
- .additionalTypes(graphQLTypes)
- .codeRegistry(codeRegistry)
- .build();
- }
+ objectSpecifications.forEach(objectSpec -> {
+ switch (objectSpec.getBeanSort()) {
- private void addToSchema(
- final ObjectSpecification objectSpec,
- final GqlvTopLevelQuery gqlvTopLevelQuery,
- final GraphQLCodeRegistry.Builder codeRegistryBuilder) {
+ case MANAGED_BEAN_CONTRIBUTING: // @DomainService
- Context context = new Context(codeRegistryBuilder, bookmarkService, specificationLoader, typeMapper, causewayConfiguration, causewaySystemEnvironment);
+ serviceRegistry.lookupBeanById(objectSpec.getLogicalTypeName())
+ .ifPresent(servicePojo ->
+ {
+ topLevelQuery.addDomainServiceTo(objectSpec, servicePojo, context);
- switch (objectSpec.getBeanSort()) {
+ });
+ break;
- case MANAGED_BEAN_CONTRIBUTING: // @DomainService
+ case ABSTRACT:
+ case VIEW_MODEL: // @DomainObject(nature=VIEW_MODEL)
+ case ENTITY: // @DomainObject(nature=ENTITY)
- addDomainServiceToTopLevelQuery(objectSpec, gqlvTopLevelQuery, context);
- break;
+ val gqlvDomainObject = new GqlvDomainObject(objectSpec, context, objectManager, graphQLTypeRegistry);
+ gqlvDomainObject.addTypesInto(graphQLTypeRegistry);
+ gqlvDomainObject.addDataFetchers();
- case ABSTRACT:
- // TODO: App interface should map to gql interfaces?
- case VIEW_MODEL: // @DomainObject(nature=VIEW_MODEL)
- case ENTITY: // @DomainObject(nature=ENTITY)
+ break;
- new GqlvDomainObject(objectSpec, context, objectManager, graphQLTypeRegistry);
+ case MANAGED_BEAN_NOT_CONTRIBUTING: // a @Service or @Component ... ignore
+ case MIXIN:
+ case VALUE:
+ case COLLECTION:
+ case VETOED:
+ case UNKNOWN:
+ break;
+ }
+ });
+ topLevelQuery.buildQueryType();
- break;
+ if (topLevelMutation != null) {
- case MANAGED_BEAN_NOT_CONTRIBUTING: // a @Service or @Component ... ignore
- case MIXIN:
- case VALUE:
- case COLLECTION:
- case VETOED:
- case UNKNOWN:
- break;
+ topLevelMutation.buildMutationType();
+ topLevelMutation.addFetchers();
}
- }
-
- public void addDomainServiceToTopLevelQuery(
- final ObjectSpecification objectSpec,
- final GqlvTopLevelQuery topLevelQueryStructure,
- final Context context) {
- serviceRegistry.lookupBeanById(objectSpec.getLogicalTypeName())
- .ifPresent(servicePojo ->
- addDomainServiceToTopLevelQuery(servicePojo, objectSpec, topLevelQueryStructure, context));
- }
-
- private void addDomainServiceToTopLevelQuery(
- final Object servicePojo,
- final ObjectSpecification objectSpec,
- final GqlvTopLevelQuery topLevelQuery,
- final Context context) {
- val domainService = new GqlvDomainService(objectSpec, servicePojo, context);
+ // finalize the fetcher/mutator code that's been registered
+ val codeRegistry = codeRegistryBuilder.build();
- boolean actionsAdded = domainService.hasActions();
- if (actionsAdded) {
- topLevelQuery.addFieldFor(domainService, context.codeRegistryBuilder);
+ // build the schema
+ val schemaBuilder = GraphQLSchema.newSchema()
+ .query(topLevelQuery.getQueryType())
+ .additionalTypes(graphQLTypeRegistry.getGraphQLTypes())
+ .codeRegistry(codeRegistry);
+ if (topLevelMutation != null) {
+ schemaBuilder.mutation(topLevelMutation.getObjectType());
}
-
-
+ return schemaBuilder
+ .build();
}
diff --git a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelQuery.java b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelMutation.java
similarity index 50%
copy from viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelQuery.java
copy to viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelMutation.java
index 36b6f983dc..c04fa400b2 100644
--- a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelQuery.java
+++ b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelMutation.java
@@ -1,10 +1,5 @@
package org.apache.causeway.viewer.graphql.viewer.toplevel;
-import org.apache.causeway.applib.services.registry.ServiceRegistry;
-import org.apache.causeway.viewer.graphql.model.domain.GqlvDomainService;
-
-import lombok.Getter;
-
import graphql.Scalars;
import graphql.schema.DataFetcher;
import graphql.schema.FieldCoordinates;
@@ -16,73 +11,78 @@ import static graphql.schema.FieldCoordinates.coordinates;
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
import static graphql.schema.GraphQLObjectType.newObject;
-public class GqlvTopLevelQuery {
+import org.apache.causeway.applib.services.registry.ServiceRegistry;
+import org.apache.causeway.viewer.graphql.model.domain.GqlvDomainService;
+
+import lombok.Getter;
+
+public class GqlvTopLevelMutation {
private final ServiceRegistry serviceRegistry;
private final GraphQLCodeRegistry.Builder codeRegistryBuilder;
- @Getter final GraphQLObjectType.Builder queryBuilder;
+ @Getter final GraphQLObjectType.Builder gqlObjectBuilder;
@Getter private GraphQLFieldDefinition numServicesField;
/**
- * Built using {@link #buildQueryType()}
+ * Built using {@link #buildMutationType()}
*/
- private GraphQLObjectType queryType;
+ private GraphQLObjectType objectType;
- public GqlvTopLevelQuery(
+ public GqlvTopLevelMutation(
final ServiceRegistry serviceRegistry,
final GraphQLCodeRegistry.Builder codeRegistryBuilder) {
this.serviceRegistry = serviceRegistry;
this.codeRegistryBuilder = codeRegistryBuilder;
- queryBuilder = newObject().name("Query");
+ gqlObjectBuilder = newObject().name("Mutation");
numServicesField = newFieldDefinition()
.name("numServices")
.type(Scalars.GraphQLInt)
.build();
- queryBuilder.field(numServicesField);
+ gqlObjectBuilder.field(numServicesField);
}
- public GraphQLObjectType buildQueryType() {
- if (queryType != null) {
- throw new IllegalStateException("QueryType has already been built");
+ public GraphQLObjectType buildMutationType() {
+ if (objectType != null) {
+ throw new IllegalStateException("Mutation type has already been built");
}
- return queryType = queryBuilder.build();
+ return objectType = gqlObjectBuilder.build();
}
/**
*
- * @see #buildQueryType()
+ * @see #buildMutationType()
*/
- public GraphQLObjectType getQueryType() {
- if (queryType == null) {
- throw new IllegalStateException("QueryType has not yet been built");
+ public GraphQLObjectType getObjectType() {
+ if (objectType == null) {
+ throw new IllegalStateException("Mutation type has not yet been built");
}
- return queryType;
+ return objectType;
}
- public void addFieldFor(
- final GqlvDomainService domainService,
- final GraphQLCodeRegistry.Builder codeRegistryBuilder) {
-
- GraphQLFieldDefinition topLevelQueryField = domainService.createTopLevelQueryField();
- queryBuilder.field(topLevelQueryField);
-
- codeRegistryBuilder.dataFetcher(
- // TODO: it would be nice to make these typesafe...
- FieldCoordinates.coordinates("Query", topLevelQueryField.getName()),
- (DataFetcher<Object>) environment -> domainService.getServicePojo());
-
- }
+// 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 addFetchers() {
codeRegistryBuilder
.dataFetcher(
- coordinates(getQueryType(), getNumServicesField()),
+ coordinates(getObjectType(), getNumServicesField()),
(DataFetcher<Object>) environment -> this.serviceRegistry.streamRegisteredBeans().count());
}
diff --git a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelQuery.java b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelQuery.java
index 36b6f983dc..71eb279b5d 100644
--- a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelQuery.java
+++ b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/toplevel/GqlvTopLevelQuery.java
@@ -1,6 +1,8 @@
package org.apache.causeway.viewer.graphql.viewer.toplevel;
import org.apache.causeway.applib.services.registry.ServiceRegistry;
+import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.viewer.graphql.model.context.Context;
import org.apache.causeway.viewer.graphql.model.domain.GqlvDomainService;
import lombok.Getter;
@@ -12,6 +14,8 @@ import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLObjectType;
+import lombok.val;
+
import static graphql.schema.FieldCoordinates.coordinates;
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
import static graphql.schema.GraphQLObjectType.newObject;
@@ -23,7 +27,6 @@ public class GqlvTopLevelQuery {
@Getter final GraphQLObjectType.Builder queryBuilder;
- @Getter private GraphQLFieldDefinition numServicesField;
/**
* Built using {@link #buildQueryType()}
@@ -38,11 +41,6 @@ public class GqlvTopLevelQuery {
this.codeRegistryBuilder = codeRegistryBuilder;
queryBuilder = newObject().name("Query");
- numServicesField = newFieldDefinition()
- .name("numServices")
- .type(Scalars.GraphQLInt)
- .build();
- queryBuilder.field(numServicesField);
}
@@ -79,11 +77,13 @@ public class GqlvTopLevelQuery {
}
- public void addFetchers() {
- codeRegistryBuilder
- .dataFetcher(
- coordinates(getQueryType(), getNumServicesField()),
- (DataFetcher<Object>) environment -> this.serviceRegistry.streamRegisteredBeans().count());
+ public void addDomainServiceTo(ObjectSpecification objectSpec, Object servicePojo, Context context) {
+ val domainService = new GqlvDomainService(objectSpec, servicePojo, context);
+
+ boolean actionsAdded = domainService.hasActions();
+ if (actionsAdded) {
+ addFieldFor(domainService, context.codeRegistryBuilder);
+ }
}
}