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 28ec90f8e16c377ea55e7d36c60e7687aa31ae66
Author: danhaywood <[email protected]>
AuthorDate: Thu Mar 14 08:49:08 2024 +0000

    CAUSEWAY-3676 : adds ability to filter GraphQL by view model
---
 .../applib/services/metamodel/BeanSort.java        |  3 +-
 .../applib/services/swagger/Visibility.java        |  4 +-
 .../core/config/CausewayConfiguration.java         | 52 +++++++++++++++++++---
 .../core/metamodel/spec/ObjectSpecification.java   | 23 ++++++++++
 .../domain/common/query/CommonDomainObject.java    | 24 +++++++++-
 .../common/query/CommonTopLevelQueryAbstract.java  | 23 +++++++---
 .../integration/GraphQlSourceForCauseway.java      |  1 -
 7 files changed, 114 insertions(+), 16 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java
index 5490a6e4bc..c87a758b41 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java
@@ -129,7 +129,7 @@ public enum BeanSort {
         return this == UNKNOWN;
     }
 
-    // -- HIGER LEVEL PREDICATES
+    // -- HIGHER LEVEL PREDICATES
 
     public boolean isToBeIntrospected() {
 
@@ -150,5 +150,6 @@ public enum BeanSort {
     }
 
 
+
     // ...
 }
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/swagger/Visibility.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/swagger/Visibility.java
index 918ccd3051..dc4d78175a 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/swagger/Visibility.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/swagger/Visibility.java
@@ -46,7 +46,7 @@ public enum Visibility {
      * The generated swagger spec is therefore restricted only to include only
      * view models ({@link DomainObject#nature()} of
      * {@link org.apache.causeway.applib.annotation.Nature#VIEW_MODEL})
-     * and to REST domain services ({@link DomainService#nature()} of
+     * and to Web API domain services ({@link DomainService#nature()} of
      * {@link NatureOfService#WEB_API}). Exposing entities also would couple 
the
      * REST client too deeply to the backend implementation.
      * </p>
@@ -60,7 +60,7 @@ public enum Visibility {
      * This visibility level removes all constraints and so includes the
      * specifications of domain entities as well as view models. This is
      * perfectly acceptable where the team developing the REST client is the
-     * same as the team developing the backend service ... the use of the REST
+     * same as the team developing the backend service ... the use of the Web
      * API between the client and server is a private implementation detail of
      * the application.
      * </p>
diff --git 
a/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
 
b/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
index b2dea70284..629157f69f 100644
--- 
a/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
+++ 
b/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
@@ -57,6 +57,8 @@ import javax.validation.constraints.Min;
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 
+import org.apache.causeway.applib.annotation.NatureOfService;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import 
org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
@@ -2364,7 +2366,7 @@ public class CausewayConfiguration {
                  *
                  * <p>
                  *     Optionally, fields for Scenario (given/when/then) 
testing may also be added if the
-                 *     {@link #isEnableScenarioTestingForRich()} config 
property is set.
+                 *     {@link Schema.Rich#isEnableScenarioTesting()} config 
property is set.
                  * </p>
                  * <p>
                  *     Suitable for clients where the application logic and 
state remains in the backend, within the
@@ -2374,8 +2376,8 @@ public class CausewayConfiguration {
                 RICH_ONLY,
                 /**
                  * Exposes both the simple and rich schemas, for the query 
have each under a field as defined by
-                 * {@link #getTopLevelFieldNameForSimple()} (by default 
&quot;simple&quot;) and
-                 * {@link #getTopLevelFieldNameForRich()} (by default 
&quot;rich&quot;).
+                 * {@link Schema.Simple#getTopLevelFieldName()} (by default 
&quot;simple&quot;) and
+                 * {@link Schema.Rich#getTopLevelFieldName()} (by default 
&quot;rich&quot;).
                  *
                  * <p>
                  *     For mutations, use the <i>simple</i> schema types.
@@ -2384,8 +2386,8 @@ public class CausewayConfiguration {
                 SIMPLE_AND_RICH,
                 /**
                  * Exposes both the simple and rich schemas, for the query 
have each under a field as defined by
-                 * {@link #getTopLevelFieldNameForSimple()} (by default 
&quot;simple&quot;) and
-                 * {@link #getTopLevelFieldNameForRich()} (by default 
&quot;rich&quot;).
+                 * {@link Schema.Simple#getTopLevelFieldName()} (by default 
&quot;simple&quot;) and
+                 * {@link Schema.Rich#getTopLevelFieldName()} (by default 
&quot;rich&quot;).
                  *
                  * <p>
                  *     For mutations, use the <i>rich</i> schema types.
@@ -2490,6 +2492,46 @@ public class CausewayConfiguration {
              */
             private ApiVariant apiVariant = ApiVariant.QUERY_AND_MUTATIONS;
 
+            /**
+             * Specifies which elements of the metamodel are included within 
the generated
+             * GraphQL spec.
+             *
+             * @since 1.x {@index}
+             */
+            public enum ApiScope {
+
+                /**
+                 * The generated GraphQL spec is restricted only to include 
only
+                 * {@link 
org.apache.causeway.applib.annotation.Nature#VIEW_MODEL view model}s.
+                 *
+                 * <p>
+                 * Applicable when the GraphQL API is in use by third-party 
clients, ie public use and not
+                 * under the control of the authors of the backend Apache 
Causeway application.
+                 * Exposing entities also would couple the GraphQL client too 
deeply to the backend implementation.
+                 * </p>
+                 */
+                VIEW_MODELS,
+                /**
+                 * The generated GraphQL spec is not restricted, includes both
+                 * {@link org.apache.causeway.applib.annotation.Nature#ENTITY 
domain entities} as well as
+                 * {@link 
org.apache.causeway.applib.annotation.Nature#VIEW_MODEL view model}s.
+                 *
+                 * <p>
+                 * This is perfectly acceptable where the team developing the 
GraphQL client is the
+                 * same as the team developing the backend service ... the use 
of the Web
+                 * API between the client and server is a private 
implementation detail of
+                 * the application.
+                 * </p>
+                 */
+                ALL,
+                ;
+            }
+
+            /**
+             * Which domain objects to include the GraphQL schema.
+             */
+            private ApiScope apiScope = ApiScope.ALL;
+
             private final MetaData metaData = new MetaData();
             @Data
             public static class MetaData {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
index 8680f5e289..01acac6fa5 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
@@ -479,11 +479,34 @@ extends
     /**
      * Includes abstract types that have
      * {@link ViewModelFacet} or {@link EntityFacet}.
+     *
+     * @see #isViewModel()
+     * @see #isEntity()
      */
     default boolean isEntityOrViewModel() {
         return isViewModel() || isEntity();
     }
 
+    /**
+     * @see #isViewModel()
+     * @see #isValue()
+     */
+    default boolean isViewModelOrValue() {
+        return isViewModel() || isValue();
+    }
+
+    /**
+     * @see #isViewModel()
+     * @see #isValue()
+     * @see #isVoid()
+     */
+    default boolean isViewModelOrValueOrVoid() {
+        return isViewModel() || isValue() || isVoid();
+    }
+
+    /**
+     * @see #getBeanSort()
+     */
     default boolean isEntityOrViewModelOrAbstract() {
         // optimized, no need to check facets
         return getBeanSort().isViewModel()
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/common/query/CommonDomainObject.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/common/query/CommonDomainObject.java
index bb06f934fe..52c7a8d15f 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/common/query/CommonDomainObject.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/common/query/CommonDomainObject.java
@@ -31,9 +31,13 @@ import static 
graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
 import static graphql.schema.GraphQLInputObjectField.newInputObjectField;
 import static graphql.schema.GraphQLInputObjectType.newInputObject;
 
+import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.core.metamodel.spec.ActionScope;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.causeway.viewer.graphql.model.context.Context;
 import org.apache.causeway.viewer.graphql.model.domain.Environment;
 import org.apache.causeway.viewer.graphql.model.domain.Element;
@@ -158,16 +162,34 @@ public class CommonDomainObject
 
 
     private void addMembers() {
-
         objectSpecification.streamProperties(MixedIn.INCLUDED)
+                .filter(this::inApiScope)
                 .forEach(prop -> 
properties.add(addChildFieldFor(schemaStrategy.newProperty(this, prop, 
context))));
         objectSpecification.streamCollections(MixedIn.INCLUDED)
+                .filter(this::inApiScope)
                 .forEach(coll -> 
collections.add(addChildFieldFor(schemaStrategy.newCollection(this, coll, 
context))));
         objectSpecification.streamActions(context.getActionScope(), 
MixedIn.INCLUDED)
+                .filter((this::inApiScope))
                 .filter(act -> 
schemaStrategy.shouldInclude(graphqlConfiguration.getApiVariant(), act))
                 .forEach(act -> 
actions.add(addChildFieldFor(schemaStrategy.newAction(this, act, context))));
     }
 
+    private boolean inApiScope(ObjectAction act) {
+        if (graphqlConfiguration.getApiScope() == 
CausewayConfiguration.Viewer.Graphql.ApiScope.ALL) {
+            return true;
+        }
+        val returnType = act.getElementType();
+        return returnType.isViewModelOrValueOrVoid() &&
+               
act.getParameterTypes().stream().allMatch(ObjectSpecification::isViewModelOrValue);
+    }
+
+    private boolean inApiScope(final ObjectAssociation objAssoc) {
+        if (graphqlConfiguration.getApiScope() == 
CausewayConfiguration.Viewer.Graphql.ApiScope.ALL) {
+            return true;
+        }
+        return objAssoc.getElementType().isViewModelOrValue();
+    }
+
     @SuppressWarnings("unused")
     private ActionScope determineActionScope() {
         return 
context.causewaySystemEnvironment.getDeploymentType().isProduction()
diff --git 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/common/query/CommonTopLevelQueryAbstract.java
 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/common/query/CommonTopLevelQueryAbstract.java
index 632ca78074..f2b6454043 100644
--- 
a/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/common/query/CommonTopLevelQueryAbstract.java
+++ 
b/viewers/graphql/model/src/main/java/org/apache/causeway/viewer/graphql/model/domain/common/query/CommonTopLevelQueryAbstract.java
@@ -23,6 +23,7 @@ import java.util.List;
 
 import graphql.schema.DataFetchingEnvironment;
 
+import org.apache.causeway.core.config.CausewayConfiguration;
 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.Element;
@@ -48,17 +49,19 @@ public abstract class CommonTopLevelQueryAbstract
         super(schemaStrategy.getSchemaType().name() + "Schema", context);
         this.schemaStrategy = schemaStrategy;
 
+        boolean includeEntities = 
context.causewayConfiguration.getViewer().getGraphql().getApiScope() == 
CausewayConfiguration.Viewer.Graphql.ApiScope.ALL;
         context.objectSpecifications().forEach(objectSpec -> {
             switch (objectSpec.getBeanSort()) {
 
                 case ABSTRACT:
-                case VIEW_MODEL: // @DomainObject(nature=VIEW_MODEL)
-                case ENTITY:     // @DomainObject(nature=ENTITY)
-
-                    val gqlvDomainObject = 
schemaStrategy.domainObjectFor(objectSpec, context);
-                    addChildField(gqlvDomainObject.newField());
-                    domainObjects.add(gqlvDomainObject);
+                case VIEW_MODEL:
+                    addDomainObject(objectSpec, schemaStrategy, context);
                     break;
+                case ENTITY:
+                    if (includeEntities) {
+                        addDomainObject(objectSpec, schemaStrategy, context);
+                    }
+
             }
         });
 
@@ -73,7 +76,15 @@ public abstract class CommonTopLevelQueryAbstract
                     break;
             }
         });
+    }
 
+    private void addDomainObject(
+            final ObjectSpecification objectSpec,
+            final SchemaStrategy schemaStrategy,
+            final Context context) {
+        val gqlvDomainObject = schemaStrategy.domainObjectFor(objectSpec, 
context);
+        addChildField(gqlvDomainObject.newField());
+        domainObjects.add(gqlvDomainObject);
     }
 
     public static List<ObjectSpecification> superclassesOf(final 
ObjectSpecification objectSpecification) {
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 4b6d7922b1..ab7d670128 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
@@ -43,7 +43,6 @@ import 
org.apache.causeway.viewer.graphql.model.toplevel.BothTopLevelQuery;
 import lombok.val;
 
 @Service()
-//@RequiredArgsConstructor(onConstructor_ = {@Inject})
 public class GraphQlSourceForCauseway implements GraphQlSource {
 
     private final CausewayConfiguration causewayConfiguration;

Reply via email to