You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2022/01/23 14:34:26 UTC

[isis] branch ISIS-2947 updated: ISIS-2947: surfaces a field on top-level 'Query' for all `@DomainService`'s.

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2947
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/ISIS-2947 by this push:
     new 4648468  ISIS-2947: surfaces a field on top-level 'Query' for all `@DomainService`'s.
4648468 is described below

commit 4648468c5464396b5757561ed8db7865b05ab31c
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Sun Jan 23 14:34:04 2022 +0000

    ISIS-2947: surfaces a field on top-level 'Query' for all `@DomainService`'s.
---
 .../load/ObjectLoader_builtinHandlers.java         |  6 +-
 .../specloader/SpecificationCacheDefault.java      |  6 +-
 .../metamodel/specloader/SpecificationLoader.java  |  3 +-
 .../specloader/SpecificationLoaderDefault.java     |  3 +-
 .../MetaModelVisitingValidatorAbstract.java        | 10 ++-
 .../viewer/source/GraphQlSourceForIsis.java        | 79 ++++++++++++++++++----
 6 files changed, 86 insertions(+), 21 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
index c6db42f..e13b578 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
@@ -97,15 +97,15 @@ final class ObjectLoader_builtinHandlers {
         public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) {
 
             val spec = objectLoadRequest.getObjectSpecification();
-            val beanName = spec.getLogicalTypeName();
+            val logicalTypeName = spec.getLogicalTypeName();
 
             val servicePojo = metaModelContext.getServiceRegistry()
-                .lookupRegisteredBeanById(beanName)
+                .lookupRegisteredBeanById(logicalTypeName)
                 .map(_ManagedBeanAdapter::getInstance)
                 .flatMap(Can::getFirst)
                 .orElseThrow(()->_Exceptions.noSuchElement(
                         "loader: %s loading beanName %s",
-                        this.getClass().getName(), beanName));
+                        this.getClass().getName(), logicalTypeName));
 
             return ManagedObject.of(spec, servicePojo);
         }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
index 0df304f..5b6033e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationCacheDefault.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.core.metamodel.specloader;
 
+import java.util.Comparator;
 import java.util.Map;
 import java.util.Optional;
 import java.util.function.Consumer;
@@ -25,6 +26,7 @@ import java.util.function.Function;
 
 import org.springframework.lang.Nullable;
 
+import org.apache.isis.applib.id.HasLogicalType;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.collections.snapshot._VersionedList;
@@ -100,7 +102,9 @@ class SpecificationCacheDefault<T extends ObjectSpecification> implements Specif
         if(shouldRunConcurrent) {
             vList.forEachParallel(onSpec);
         } else {
-            vList.forEach(onSpec);
+            vList
+                .stream().sorted(Comparator.comparing(HasLogicalType::getLogicalTypeName))
+                .forEach(onSpec);
         }
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
index fec274a..fa657a2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
@@ -107,7 +107,7 @@ public interface SpecificationLoader {
      *
      * @param action
      */
-    void forEach(Consumer<ObjectSpecification> onSpec);
+    void forEach(Consumer<ObjectSpecification> onSpec, final boolean shouldRunConcurrent);
 
     void reloadSpecification(Class<?> domainType);
 
@@ -261,4 +261,5 @@ public interface SpecificationLoader {
                         featureIdentifier));
     }
 
+    boolean isMetamodelFullyIntrospected();
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java
index 9863227..d41a73b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderDefault.java
@@ -445,8 +445,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     }
 
     @Override
-    public void forEach(final Consumer<ObjectSpecification> onSpec) {
-        val shouldRunConcurrent = isisConfiguration.getCore().getMetaModel().getValidator().isParallelize();
+    public void forEach(final Consumer<ObjectSpecification> onSpec, final boolean shouldRunConcurrent) {
         cache.forEach(onSpec, shouldRunConcurrent);
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelVisitingValidatorAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelVisitingValidatorAbstract.java
index cfe8c39..266b678 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelVisitingValidatorAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelVisitingValidatorAbstract.java
@@ -18,8 +18,11 @@
  */
 package org.apache.isis.core.metamodel.specloader.validator;
 
+import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 
+import lombok.val;
+
 public abstract class MetaModelVisitingValidatorAbstract
 extends MetaModelValidatorAbstract
 implements MetaModelVisitingValidator {
@@ -35,8 +38,11 @@ implements MetaModelVisitingValidator {
             return;
         }
 
-        super.getMetaModelContext().getSpecificationLoader()
-        .forEach(this::validate);
+        val shouldRunConcurrent =
+                getMetaModelContext().getConfiguration().getCore().getMetaModel().getValidator().isParallelize();
+
+        getMetaModelContext().getSpecificationLoader()
+        .forEach(this::validate, shouldRunConcurrent);
 
         summarize();
 
diff --git a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/GraphQlSourceForIsis.java b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/GraphQlSourceForIsis.java
index 82cd012..d0a0f49 100644
--- a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/GraphQlSourceForIsis.java
+++ b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/GraphQlSourceForIsis.java
@@ -1,18 +1,20 @@
 package org.apache.isis.viewer.graphql.viewer.source;
 
-import java.util.concurrent.CountDownLatch;
-
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 
 import org.springframework.graphql.execution.GraphQlSource;
 import org.springframework.stereotype.Service;
 
+import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
 import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.core.config.environment.IsisSystemEnvironment;
 import org.apache.isis.core.config.metamodel.specloader.IntrospectionMode;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoaderDefault;
+import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFacet;
+import org.apache.isis.core.metamodel.spec.feature.MixedIn;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
 import lombok.RequiredArgsConstructor;
 import lombok.val;
@@ -21,25 +23,25 @@ import graphql.GraphQL;
 import graphql.Scalars;
 import graphql.execution.instrumentation.tracing.TracingInstrumentation;
 import graphql.schema.DataFetcher;
+import graphql.schema.FieldCoordinates;
 import graphql.schema.GraphQLCodeRegistry;
-import graphql.schema.GraphQLFieldDefinition;
 import graphql.schema.GraphQLObjectType;
 import graphql.schema.GraphQLSchema;
 
 import static graphql.schema.FieldCoordinates.coordinates;
+import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
+import static graphql.schema.GraphQLObjectType.newObject;
 
 @Service()
 @RequiredArgsConstructor(onConstructor_ = {@Inject})
 public class GraphQlSourceForIsis implements GraphQlSource {
 
     private final ServiceRegistry serviceRegistry;
-    private final SpecificationLoaderDefault specificationLoader;
+    private final SpecificationLoader specificationLoader;
     private final IsisConfiguration isisConfiguration;
     private final IsisSystemEnvironment isisSystemEnvironment;
     private final ExecutionStrategyResolvingWithinInteraction executionStrategy;
 
-    private final CountDownLatch countDownLatch = new CountDownLatch(1);
-
     @PostConstruct
     public void init() {
         boolean fullyIntrospect = IntrospectionMode.isFullIntrospect(isisConfiguration, isisSystemEnvironment);
@@ -51,7 +53,7 @@ public class GraphQlSourceForIsis implements GraphQlSource {
     @Override
     public GraphQL graphQl() {
         return GraphQL.newGraphQL(schema())
-                .instrumentation(new TracingInstrumentation())
+                // .instrumentation(new TracingInstrumentation())
                 .queryExecutionStrategy(executionStrategy)
                 .build();
     }
@@ -95,20 +97,73 @@ public class GraphQlSourceForIsis implements GraphQlSource {
 //                        (DataFetcher<Object>) environment -> leaseRepository.numLeases)
 //                .build();
 
+        val queryBuilder = newObject().name("Query");
+        val codeRegistryBuilder = GraphQLCodeRegistry.newCodeRegistry();
+
+        specificationLoader.forEach(objectSpecification -> {
+
+            val logicalTypeName = objectSpecification.getLogicalTypeName();
+            val correspondingClass = objectSpecification.getCorrespondingClass();
+            switch (objectSpecification.getBeanSort()) {
+                case VIEW_MODEL: // @DomainObject(nature=VIEW_MODEL)
+                    // TODO
+                    break;
+                case ENTITY:    // @DomainObject(nature=ENTITY)
+                    // TODO
+                    break;
+                case MANAGED_BEAN_CONTRIBUTING: //@DomainService
+
+                    serviceRegistry.lookupBeanById(logicalTypeName).ifPresent(service -> {
+
+                        String logicalTypeNameSanitized = logicalTypeName.replace('.', '_');
+
+                        // as a first pass, expose a single "property" of the service, which is just a count of how
+                        // many safe actions there are
+                        val serviceAsGraphQlType = newObject().name(logicalTypeNameSanitized)
+                                .field(newFieldDefinition()
+                                        .name("numSafeActions")
+                                        .type(Scalars.GraphQLInt)
+                                        .build()).build();
+                        codeRegistryBuilder.dataFetcher(
+                                FieldCoordinates.coordinates(serviceAsGraphQlType, "numSafeActions"),
+                                (DataFetcher<Object>) environment -> objectSpecification.streamRuntimeActions(MixedIn.INCLUDED)
+                                        .map(ObjectAction.class::cast)
+                                        .filter((ObjectAction x) -> x.containsFacet(ActionSemanticsFacet.class))
+                                        .map(x -> x.getFacet(ActionSemanticsFacet.class))
+                                        .map(x -> x.value() == SemanticsOf.SAFE)
+                                        .count());
+
+                        // make the service accessible from the top-level Query
+                        queryBuilder.field(newFieldDefinition().name(logicalTypeNameSanitized).type(serviceAsGraphQlType).build());
+                        codeRegistryBuilder.dataFetcher(FieldCoordinates.coordinates("Query", newFieldDefinition().name(logicalTypeNameSanitized).type(serviceAsGraphQlType).build().getName()), (DataFetcher<Object>) environment -> service);
+                    });
+                    break;
+
+                case MANAGED_BEAN_NOT_CONTRIBUTING: // a @Service or @Component ... ignore
+                case MIXIN:
+                case VALUE:
+                case COLLECTION:
+                case ABSTRACT:
+                case VETOED:
+                case UNKNOWN:
+                    break;
+            }
+        }, false);
+
         // type Query {
         //     numServices: Int
         // }
-        val query_numServices = GraphQLFieldDefinition.newFieldDefinition()
+        val query_numServices = newFieldDefinition()
                 .name("numServices")
                 .type(Scalars.GraphQLInt)
                 .build();
-        GraphQLObjectType query = GraphQLObjectType.newObject()
-                .name("Query")
+
+        GraphQLObjectType query = queryBuilder
                 .field(query_numServices)
                 .build();
 
 
-        val codeRegistry = GraphQLCodeRegistry.newCodeRegistry()
+        val codeRegistry = codeRegistryBuilder
         .dataFetcher(coordinates(query.getName(), query_numServices.getName()),
                 (DataFetcher<Object>) environment -> this.serviceRegistry.streamRegisteredBeans().count())
         .build();