You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/10/11 21:45:23 UTC

[isis] branch v2 updated: ISIS-1975: RO: allows fine-grained property suppression

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

ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/v2 by this push:
     new 714b3da  ISIS-1975: RO: allows fine-grained property suppression
714b3da is described below

commit 714b3da49230d76524e75c5de098d1ad902f3e0b
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Oct 11 23:44:54 2018 +0200

    ISIS-1975: RO: allows fine-grained property suppression
    
    ... without breaking backward compatibility
    
    Task-Url: https://issues.apache.org/jira/browse/ISIS-1975
---
 .../services/swagger/internal/Generation.java      |   4 +
 .../conneg/ContentNegotiationServiceAbstract.java  |  16 +++
 .../ContentNegotiationServiceOrgApacheIsisV1.java  | 116 +++++++++++++++------
 3 files changed, 107 insertions(+), 29 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
index 335d944..7707b08 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
@@ -382,6 +382,7 @@ class Generation {
                 .type("string"))
         .produces("application/json;profile=urn:org.apache.isis/v1")
         .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+        .produces("application/json;profile=urn:org.apache.isis/v1;suppress=all")
         .produces("application/json;profile=urn:org.restfulobjects:repr-types/object");
 
 
@@ -456,6 +457,7 @@ class Generation {
                 .description(Util.roSpec("19.1") + ": (invoke) resource of " + serviceId + "#" + actionId)
                 .produces("application/json;profile=urn:org.apache.isis/v1")
                 .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+                .produces("application/json;profile=urn:org.apache.isis/v1;suppress=all")
                 .produces("application/json;profile=urn:org.restfulobjects:repr-types/object")
                 .produces("application/json;profile=urn:org.restfulobjects:repr-types/action-result")
                 ;
@@ -544,6 +546,7 @@ class Generation {
                         .type("string"))
                 .produces("application/json;profile=urn:org.apache.isis/v1")
                 .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+                .produces("application/json;profile=urn:org.apache.isis/v1;suppress=all")
                 .produces("application/json;profile=urn:org.restfulobjects:repr-types/object-collection");
 
         path.get(collectionOperation);
@@ -577,6 +580,7 @@ class Generation {
                         .type("string"))
                 .produces("application/json;profile=urn:org.apache.isis/v1")
                 .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+                .produces("application/json;profile=urn:org.apache.isis/v1;suppress=all")
                 .produces("application/json;profile=urn:org.restfulobjects:repr-types/action-result");
 
         final SemanticsOf semantics = objectAction.getSemantics();
diff --git a/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceAbstract.java b/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceAbstract.java
index 3bdf38b..af3ec5e 100644
--- a/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceAbstract.java
+++ b/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceAbstract.java
@@ -29,6 +29,8 @@ import javax.ws.rs.core.Response;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.core.commons.factory.InstanceUtil;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse;
@@ -142,6 +144,20 @@ public abstract class ContentNegotiationServiceAbstract implements ContentNegoti
         }
         return false;
     }
+    
+    protected List<String> mediaTypeParameterList(
+            final List<MediaType> acceptableMediaTypes,
+            final String parameter) {
+        final List<String> paramList = _Lists.newArrayList();
+        for (MediaType mediaType : acceptableMediaTypes) {
+            final String paramValue = sanitize(mediaType.getParameters().get(parameter));
+            _Strings.splitThenStream(paramValue, ",")
+            .map(String::trim)
+            .forEach(paramList::add);
+        }
+        return paramList;
+    }
+    
 
     /**
      * Remove any single quotes.
diff --git a/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceOrgApacheIsisV1.java b/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceOrgApacheIsisV1.java
index fed50d5..94ba534 100644
--- a/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceOrgApacheIsisV1.java
+++ b/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceOrgApacheIsisV1.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.viewer.restfulobjects.rendering.service.conneg;
 
+import java.util.EnumSet;
 import java.util.List;
 import java.util.stream.Stream;
 
@@ -28,6 +29,7 @@ import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.consent.Consent;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
@@ -51,6 +53,52 @@ import org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationSer
         menuOrder = "" + (Integer.MAX_VALUE - 10)
         )
 public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiationServiceAbstract {
+    
+    public static enum SuppressionType {
+        
+        /** suppress '$$RO', RO Spec representation*/
+        RO,
+        
+        /** suppress '$$href', hyperlink to the representation*/
+        HREF,
+        
+        /** suppress '$$instanceId', instance id of the domain object*/
+        ID,
+        
+        /** suppress '$$title', title of the domain object*/
+        TITLE,
+        
+        /** suppress all '$$...' entries*/
+        ALL
+        ;
+
+        public static EnumSet<SuppressionType> parse(List<String> parameterList) {
+            final EnumSet<SuppressionType> set = EnumSet.noneOf(SuppressionType.class);
+            parameterList.stream()
+            .map(SuppressionType::parseOrElseNull)
+            .filter(_NullSafe::isPresent)
+            .forEach(set::add);
+            if(set.contains(ALL)) {
+                return EnumSet.allOf(SuppressionType.class);
+            }
+            return set;
+        }
+        
+        private static SuppressionType parseOrElseNull(String literal) {
+            
+            // honor pre v2 behavior
+            if("true".equalsIgnoreCase(literal)) {
+                return SuppressionType.RO; 
+            }
+            
+            try {
+                return SuppressionType.valueOf(literal.toUpperCase());
+            } catch (IllegalArgumentException  e) {
+                return null;
+            }
+        }
+        
+    }
 
     /**
      * Unlike RO v1.0, use a single content-type of <code>application/json;profile="urn:org.apache.isis/v1"</code>.
@@ -63,11 +111,6 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
     public static final String ACCEPT_PROFILE = "urn:org.apache.isis/v1";
 
     /**
-     * Whether to suppress the RO v1.0 '$$ro' node; suppress='false' or suppress='true'
-     */
-    public static final String ACCEPT_RO = "suppress";
-
-    /**
      * The media type (as a string) used as the content-Type header when a domain object is rendered.
      *
      * @see #ACCEPT_PROFILE for discussion.
@@ -110,11 +153,13 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
         if(!canAccept) {
             return null;
         }
-        boolean suppressRO = suppress(rendererContext);
+        
+        final EnumSet<SuppressionType> suppression = suppress(rendererContext);
+        final boolean suppressRO = suppression.contains(SuppressionType.RO);
 
         final JsonRepresentation rootRepresentation = JsonRepresentation.newMap();
 
-        appendObjectTo(rendererContext, objectAdapter, rootRepresentation);
+        appendObjectTo(rendererContext, objectAdapter, rootRepresentation, suppression);
 
         final JsonRepresentation $$roRepresentation;
         if(!suppressRO) {
@@ -157,14 +202,15 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
         if(!canAccept(rendererContext)) {
             return null;
         }
-        boolean suppressRO = suppress(rendererContext);
+        final EnumSet<SuppressionType> suppression = suppress(rendererContext);
+        final boolean suppressRO = suppression.contains(SuppressionType.RO);
 
         final JsonRepresentation rootRepresentation = JsonRepresentation.newArray();
 
         ObjectAdapter objectAdapter = objectAndCollection.getObjectAdapter();
         OneToManyAssociation collection = objectAndCollection.getMember();
 
-        appendCollectionTo(rendererContext, objectAdapter, collection, rootRepresentation);
+        appendCollectionTo(rendererContext, objectAdapter, collection, rootRepresentation, suppression);
 
         final JsonRepresentation $$roRepresentation;
         if(!suppressRO) {
@@ -216,7 +262,8 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
         if(!canAccept(rendererContext)) {
             return null;
         }
-        boolean suppressRO = suppress(rendererContext);
+        final EnumSet<SuppressionType> suppression = suppress(rendererContext);
+        final boolean suppressRO = suppression.contains(SuppressionType.RO);
 
         JsonRepresentation rootRepresentation = null;
         final JsonRepresentation $$roRepresentation;
@@ -238,7 +285,7 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
         case DOMAIN_OBJECT:
 
             rootRepresentation = JsonRepresentation.newMap();
-            appendObjectTo(rendererContext, returnedAdapter, rootRepresentation);
+            appendObjectTo(rendererContext, returnedAdapter, rootRepresentation, suppression);
 
             break;
 
@@ -250,7 +297,7 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
             
             final Stream<ObjectAdapter> collectionAdapters = CollectionFacet.Utils.streamAdapters(returnedAdapter);
             
-            appendStreamTo(rendererContext, collectionAdapters, rootRepresentation);
+            appendStreamTo(rendererContext, collectionAdapters, rootRepresentation, suppression);
 
             // $$ro representation will be an object in the list with a single property named "$$ro"
             if(!suppressRO) {
@@ -294,18 +341,19 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
         return mediaTypeParameterMatches(acceptableMediaTypes, "profile", ACCEPT_PROFILE);
     }
 
-    protected boolean suppress(
+    protected EnumSet<SuppressionType> suppress(
             final RepresentationService.Context2 rendererContext) {
         final List<MediaType> acceptableMediaTypes = rendererContext.getAcceptableMediaTypes();
-        return mediaTypeParameterMatches(acceptableMediaTypes, "suppress", "true");
+        return SuppressionType.parse(mediaTypeParameterList(acceptableMediaTypes, "suppress"));
     }
-
+    
     private void appendObjectTo(
             final RepresentationService.Context2 rendererContext,
             final ObjectAdapter objectAdapter,
-            final JsonRepresentation rootRepresentation) {
+            final JsonRepresentation rootRepresentation,
+            final EnumSet<SuppressionType> suppression) {
 
-        appendPropertiesTo(rendererContext, objectAdapter, rootRepresentation);
+        appendPropertiesTo(rendererContext, objectAdapter, rootRepresentation, suppression);
 
         final Where where = rendererContext.getWhere();
         final Stream<OneToManyAssociation> collections = objectAdapter.getSpecification()
@@ -322,7 +370,7 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
                 return;
             }
 
-            appendCollectionTo(rendererContext, objectAdapter, collection, collectionRepresentation);
+            appendCollectionTo(rendererContext, objectAdapter, collection, collectionRepresentation, suppression);
         });
         
     }
@@ -330,7 +378,8 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
     private void appendPropertiesTo(
             final RepresentationService.Context2 rendererContext,
             final ObjectAdapter objectAdapter,
-            final JsonRepresentation rootRepresentation) {
+            final JsonRepresentation rootRepresentation,
+            final EnumSet<SuppressionType> suppression) {
         final InteractionInitiatedBy interactionInitiatedBy = determineInteractionInitiatedByFrom(rendererContext);
         final Where where = rendererContext.getWhere();
         final Stream<OneToOneAssociation> properties = objectAdapter.getSpecification()
@@ -350,12 +399,19 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
 
             final JsonRepresentation propertyValueRepresentation = renderer.render();
 
-            final String upHref = propertyValueRepresentation.getString("links[rel=up].href");
-            rootRepresentation.mapPut("$$href", upHref);
-            final String upTitle = propertyValueRepresentation.getString("links[rel=up].title");
-            rootRepresentation.mapPut("$$title", upTitle);
-            final String upInstanceId = upHref.substring(upHref.lastIndexOf("/")+1);
-            rootRepresentation.mapPut("$$instanceId", upInstanceId);
+            if(!suppression.contains(SuppressionType.HREF)) {
+                final String upHref = propertyValueRepresentation.getString("links[rel=up].href");
+                rootRepresentation.mapPut("$$href", upHref);
+            }
+            if(!suppression.contains(SuppressionType.TITLE)) {
+                final String upTitle = propertyValueRepresentation.getString("links[rel=up].title");
+                rootRepresentation.mapPut("$$title", upTitle);
+            }
+            if(!suppression.contains(SuppressionType.ID)) {
+                final String upHref = propertyValueRepresentation.getString("links[rel=up].href");
+                final String upInstanceId = upHref.substring(upHref.lastIndexOf("/")+1);
+                rootRepresentation.mapPut("$$instanceId", upInstanceId);
+            }
 
             final JsonRepresentation value = propertyValueRepresentation.getRepresentation("value");
             rootRepresentation.mapPut(property.getId(), value);
@@ -367,7 +423,8 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
             final RepresentationService.Context2 rendererContext,
             final ObjectAdapter objectAdapter,
             final OneToManyAssociation collection,
-            final JsonRepresentation representation) {
+            final JsonRepresentation representation, 
+            final EnumSet<SuppressionType> suppression) {
 
         final InteractionInitiatedBy interactionInitiatedBy = determineInteractionInitiatedByFrom(rendererContext);
         final ObjectAdapter valueAdapter = collection.get(objectAdapter, interactionInitiatedBy);
@@ -376,17 +433,18 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
         }
 
         final Stream<ObjectAdapter> adapters = CollectionFacet.Utils.streamAdapters(valueAdapter);
-        appendStreamTo(rendererContext, adapters, representation);
+        appendStreamTo(rendererContext, adapters, representation, suppression);
     }
 
     private void appendStreamTo(
             final RepresentationService.Context2 rendererContext,
             final Stream<ObjectAdapter> adapters,
-            final JsonRepresentation collectionRepresentation) {
+            final JsonRepresentation collectionRepresentation, 
+            final EnumSet<SuppressionType> suppression) {
         
         adapters.forEach(elementAdapter->{
             JsonRepresentation elementRepresentation = JsonRepresentation.newMap();
-            appendPropertiesTo(rendererContext, elementAdapter, elementRepresentation);
+            appendPropertiesTo(rendererContext, elementAdapter, elementRepresentation, suppression);
 
             collectionRepresentation.arrayAdd(elementRepresentation);
         });