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 2022/04/12 21:08:26 UTC

[isis] branch master updated: ISIS-2999: improve restclient api

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

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


The following commit(s) were added to refs/heads/master by this push:
     new ed8f200ae6 ISIS-2999: improve restclient api
ed8f200ae6 is described below

commit ed8f200ae630f2f5ff5b397147b58dfe506dd459
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Apr 12 23:08:21 2022 +0200

    ISIS-2999: improve restclient api
    
    - don't expose ResponseDigest (implementation detail)
---
 .../secondary/fetch/_LegacyClient.java             |  16 +-
 .../isis/testdomain/rest/CorsFilterTest.java       |  23 +--
 .../RestServiceSimpifiedRepresentationTest.java    | 209 ++++++++++-----------
 .../shiro/ShiroSecmanLdap_restfulStressTest.java   |  10 +-
 .../testdomain/rest/RestServiceStressTest.java     |  11 +-
 .../isis/testdomain/rest/RestServiceTest.java      |  59 ++----
 .../testdomain/util/rest/RestEndpointService.java  |  16 +-
 .../restfulobjects/client/ResponseDigest.java      |   7 +-
 .../restfulobjects/client/RestfulClient.java       |  25 ++-
 9 files changed, 168 insertions(+), 208 deletions(-)

diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/fetch/_LegacyClient.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/fetch/_LegacyClient.java
index fca71cabbc..a4fcb0d2b4 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/fetch/_LegacyClient.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/fetch/_LegacyClient.java
@@ -21,6 +21,7 @@ package org.apache.isis.extensions.commandreplay.secondary.fetch;
 import java.net.URI;
 
 import lombok.Getter;
+import lombok.RequiredArgsConstructor;
 import lombok.experimental.UtilityClass;
 
 @Deprecated
@@ -33,17 +34,12 @@ class _LegacyClient {
     }
 
     public static interface JaxRsClient {
+        @RequiredArgsConstructor
         public enum ReprType {
-            OBJECT("object"),
             ACTION_RESULT("action-result");
-            @Getter
-            private final String suffix;
-            ReprType(final String suffix) {
-                this.suffix = suffix;
-            }
+            @Getter private final String suffix;
         }
         JaxRsResponse get(URI uri, Class<?> dtoClass, ReprType reprType, String username, String password);
-        JaxRsResponse post(URI uri, String bodyJson, String username, String password);
     }
 
     public static class JaxRsClientDefault implements JaxRsClient {
@@ -54,12 +50,6 @@ class _LegacyClient {
             return null;
         }
 
-        @Override
-        public JaxRsResponse post(final URI uri, final String bodyJson, final String username, final String password) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
     }
 
 
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/CorsFilterTest.java b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/CorsFilterTest.java
index 4d2235f8dc..303c938e0d 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/CorsFilterTest.java
+++ b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/CorsFilterTest.java
@@ -22,6 +22,7 @@ import java.util.function.UnaryOperator;
 
 import javax.inject.Inject;
 
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
 import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Test;
@@ -32,18 +33,16 @@ import org.springframework.context.annotation.Import;
 import org.springframework.test.context.TestPropertySource;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.functional.Try;
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.extensions.cors.impl.IsisModuleExtCors;
 import org.apache.isis.testdomain.conf.Configuration_headless;
 import org.apache.isis.testdomain.rospec.Configuration_usingRoSpec;
 import org.apache.isis.testdomain.rospec.RoSpecSampler;
 import org.apache.isis.testdomain.util.rest.RestEndpointService;
-import org.apache.isis.viewer.restfulobjects.client.ResponseDigest;
 import org.apache.isis.viewer.restfulobjects.client.log.ClientConversationFilter;
 import org.apache.isis.viewer.restfulobjects.jaxrsresteasy4.IsisModuleViewerRestfulObjectsJaxrsResteasy4;
 
@@ -106,7 +105,7 @@ class CorsFilterTest {
 
     // -- HELPER
 
-    <T> ResponseDigest<T> digestUsingPost(
+    <T> Try<T> digestUsingPost(
             final String actionName,
             final Class<T> entityType,
             final UnaryOperator<javax.ws.rs.client.Invocation.Builder> onRequestBuilder) {
@@ -130,7 +129,7 @@ class CorsFilterTest {
 
     }
 
-    <T> ResponseDigest<T> digestUsingGet(
+    <T> Try<T> digestUsingGet(
             final String actionName,
             final Class<T> entityType,
             final UnaryOperator<javax.ws.rs.client.Invocation.Builder> onRequestBuilder) {
@@ -159,17 +158,15 @@ class CorsFilterTest {
         return "http://localhost";
     }
 
-    private void assertHttpResponse200(final ResponseDigest<String> digest) {
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
-        val returnValue = digest.getEntities().getSingletonOrFail();
+    private void assertHttpResponse200(final Try<String> digest) {
+        digest.ifFailure(Assertions::fail);
+        val returnValue = digest.getValue().orElseThrow();
         assertEquals(refSampler.string(), returnValue);
     }
 
-    private void assertHttpResponse403(final ResponseDigest<String> digest) {
-        assertNotNull(digest.getFailureCause(), "request was expected to fail, but succeeded");
-        assertTrue(digest.getFailureCause().getMessage().contains("403"));
+    private void assertHttpResponse403(final Try<String> digest) {
+        assertTrue(digest.getFailure().isPresent(), "request was expected to fail, but succeeded");
+        assertTrue(digest.getFailure().get().getMessage().contains("403"));
     }
 
 }
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/RestServiceSimpifiedRepresentationTest.java b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/RestServiceSimpifiedRepresentationTest.java
index b5584760e7..7bb5306995 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/RestServiceSimpifiedRepresentationTest.java
+++ b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/RestServiceSimpifiedRepresentationTest.java
@@ -32,6 +32,7 @@ import org.asciidoctor.ast.Document;
 import org.asciidoctor.ast.Row;
 import org.asciidoctor.ast.Table;
 import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
 import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Test;
@@ -43,10 +44,10 @@ import org.springframework.test.context.TestPropertySource;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 
 import org.apache.isis.applib.client.RepresentationTypeSimplifiedV2;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.functional.Try;
 import org.apache.isis.commons.internal.debug._Probe;
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.testdomain.conf.Configuration_headless;
@@ -59,7 +60,6 @@ import org.apache.isis.testdomain.util.rest.RestEndpointService;
 import org.apache.isis.tooling.model4adoc.AsciiDocFactory;
 import org.apache.isis.tooling.model4adoc.AsciiDocWriter;
 import org.apache.isis.viewer.restfulobjects.client.ActionParameterListBuilder;
-import org.apache.isis.viewer.restfulobjects.client.ResponseDigest;
 import org.apache.isis.viewer.restfulobjects.client.log.ClientConversationFilter;
 import org.apache.isis.viewer.restfulobjects.jaxrsresteasy4.IsisModuleViewerRestfulObjectsJaxrsResteasy4;
 
@@ -99,10 +99,10 @@ class RestServiceSimpifiedRepresentationTest {
     private final Filter4ReprType filter4ReprType = new Filter4ReprType();
     private final Filter4Reporting filter4Reporting = new Filter4Reporting();
     private final Can<ClientConversationFilter> conversationFilters = Can.of(
-            filter4ReprType, 
+            filter4ReprType,
             filter4Reporting);
 
-    
+
     @AfterAll
     static void tearDown() throws IOException {
     	if(UtilSurefire.isRunningWithSurefire()) {
@@ -116,27 +116,27 @@ class RestServiceSimpifiedRepresentationTest {
 
     // -- VOID
 
-    @Test @Order(1) 
+    @Test @Order(1)
     void voidResult() {
         val digest = digest("voidResult", void.class);
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().isEmpty());
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VOID);
     }
 
     // -- STRING
 
-    @Test @Order(2) 
+    @Test @Order(2)
     void string() {
         val digest = digest("string", String.class);
-        val returnValue = digest.getEntities().getSingletonOrFail();
+        val returnValue = digest.getValue().orElseThrow();
         assertEquals(refSampler.string(), returnValue);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUE);
     }
-    
-    @Test @Order(2) 
+
+    @Test @Order(2)
     void stringUsingGet() {
         val digest = digestUsingGet("stringSafe", String.class);
-        val returnValue = digest.getEntities().getSingletonOrFail();
+        val returnValue = digest.getValue().orElseThrow();
         assertEquals(refSampler.stringSafe(), returnValue);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUE);
     }
@@ -144,7 +144,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(3)
     void stringNull() {
         val digest = digest("stringNull", String.class);
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().orElseThrow().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -154,7 +154,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(4)
     void stringArray() {
         val digest = digestArray("stringArray", String.class, new GenericType<List<String>>(){});
-        val returnValue = digest.getEntities();
+        val returnValue = digest.getValue().orElseThrow();
         assertComponentWiseEquals(refSampler.stringArray(), returnValue);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUES);
     }
@@ -162,7 +162,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(5)
     void stringArrayEmpty() {
         val digest = digestArray("stringArrayEmpty", String.class, new GenericType<List<String>>(){});
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().orElseThrow().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -170,7 +170,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(6)
     void stringArrayNull() {
         val digest = digestArray("stringArrayNull", String.class, new GenericType<List<String>>(){});
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().orElseThrow().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -180,7 +180,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(7)
     void stringList() {
         val digest = digestList("stringList", String.class, new GenericType<List<String>>(){});
-        val returnValues = digest.getEntities();
+        val returnValues = digest.getValue().orElseThrow();
         assertComponentWiseEquals(refSampler.stringList(), returnValues);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUES);
     }
@@ -188,7 +188,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(8)
     void stringListEmpty() {
         val digest = digestList("stringListEmpty", String.class, new GenericType<List<String>>(){});
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().orElseThrow().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -196,7 +196,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(9)
     void stringListNull() {
         val digest = digestList("stringListNull", String.class, new GenericType<List<String>>(){});
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().orElseThrow().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -206,7 +206,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(10)
     void integer() {
         val digest = digest("integer", Integer.class);
-        val returnValue = digest.getEntities().getSingletonOrFail();
+        val returnValue = digest.getValue().orElseThrow();
         assertEquals(refSampler.integer(), returnValue);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUE);
     }
@@ -214,7 +214,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(11)
     void integerNull() {
         val digest = digest("integerNull", Integer.class);
-        assertTrue(digest.getEntities().isEmpty());       
+        assertTrue(digest.getValue().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -222,7 +222,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(12)
     void integerPrimitive() {
         val digest = digest("integerPrimitive", int.class);
-        val returnValue = digest.getEntities().getSingletonOrFail();
+        val returnValue = digest.getValue().orElseThrow();
         assertEquals(refSampler.integerPrimitive(), returnValue);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUE);
     }
@@ -232,7 +232,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(13)
     void bigInteger() {
         val digest = digest("bigInteger", BigInteger.class);
-        val returnValue = digest.getEntities().getSingletonOrFail();
+        val returnValue = digest.getValue().orElseThrow();
         assertEquals(refSampler.bigInteger(), returnValue);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUE);
     }
@@ -240,7 +240,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(14)
     void bigIntegerNull() {
         val digest = digest("bigIntegerNull", BigInteger.class);
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -248,7 +248,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(15)
     void bigIntegerList() {
         val digest = digestList("bigIntegerList", BigInteger.class, new GenericType<List<BigInteger>>(){});
-        val returnValue = digest.getEntities();
+        val returnValue = digest.getValue().orElseThrow();
         assertComponentWiseNumberEquals(refSampler.bigIntegerList(), returnValue);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUES);
     }
@@ -258,7 +258,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(16)
     void customer() {
         val digest = digest("customer", Customer.class);
-        val returnValue = digest.getEntities().getSingletonOrFail();
+        val returnValue = digest.getValue().orElseThrow();
         assertEquals(refSampler.customer(), returnValue);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.OBJECT);
     }
@@ -266,7 +266,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(17)
     void customerNull() {
         val digest = digest("customerNull", Customer.class);
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -274,7 +274,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(18)
     void customerList() {
         val digest = digestList("customerList", Customer.class, new GenericType<List<Customer>>(){});
-        val returnValues = digest.getEntities();
+        val returnValues = digest.getValue().orElseThrow();
         assertComponentWiseEquals(refSampler.customerList(), returnValues);
         filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.LIST);
     }
@@ -282,7 +282,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(19)
     void customerListEmpty() {
         val digest = digestList("customerListEmpty", Customer.class, new GenericType<List<Customer>>(){});
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().orElseThrow().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -290,7 +290,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(20)
     void customerListNull() {
         val digest = digestList("customerListNull", Customer.class, new GenericType<List<Customer>>(){});
-        assertTrue(digest.getEntities().isEmpty());
+        assertTrue(digest.getValue().orElseThrow().isEmpty());
         filter4ReprType.assertHttpReturnCode(404);
         filter4ReprType.assertRepresentationType(null);
     }
@@ -300,7 +300,7 @@ class RestServiceSimpifiedRepresentationTest {
     @Test @Order(21)
     void complexList() {
         val digest = digestList("complexList", BigComplex.class, new GenericType<List<BigComplex>>(){});
-        val returnValue = digest.getEntities();
+        val returnValue = digest.getValue().orElseThrow();
         assertComponentWiseEquals(refSampler.complexList(), returnValue);
         // in practice we might be agnostic to the actual representation type here, as long as we can successfully digest
         //filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUES);
@@ -309,9 +309,9 @@ class RestServiceSimpifiedRepresentationTest {
 
     @Test @Order(22)
     void complexAdd() {
-        // given 
+        // given
         val a = BigComplex.of(
-                "1.0000000000000000000000000000000000000001", 
+                "1.0000000000000000000000000000000000000001",
                 "-2.0000000000000000000000000000000000000002");
         val b = BigComplex.of("3", "4");
 
@@ -321,7 +321,7 @@ class RestServiceSimpifiedRepresentationTest {
             argBuilder.addActionParameter("bre", b.getRe().toPlainString());
             argBuilder.addActionParameter("bim", b.getIm().toPlainString());
         });
-        val returnValue = digest.getEntities().getSingletonOrFail();
+        val returnValue = digest.getValue().orElseThrow();
         BigComplex.assertEquals(a.add(b), returnValue);
         // in practice we might be agnostic to the actual representation type here, as long as we can successfully digest
         //filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUE);
@@ -330,120 +330,111 @@ class RestServiceSimpifiedRepresentationTest {
 
     // -- HELPER
 
-    <T> ResponseDigest<T> digest(String actionName, Class<T> entityType) {
+    <T> Try<T> digest(final String actionName, final Class<T> entityType) {
         return digest(actionName, entityType, argBuilder->{});
     }
 
-    <T> ResponseDigest<T> digest(String actionName, Class<T> entityType, Consumer<ActionParameterListBuilder> onArgs) {
+    <T> Try<T> digest(final String actionName, final Class<T> entityType, final Consumer<ActionParameterListBuilder> onArgs) {
 
         _Probe.errOut("");
         _Probe.errOut("=== %s", actionName);
         _Probe.errOut("");
-        
+
         assertTrue(restService.getPort()>0);
 
         val useRequestDebugLogging = false;
         val client = restService.newClient(useRequestDebugLogging, conversationFilters);
 
-        val request = restService.newInvocationBuilder(client, 
-                String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName)); 
+        val request = restService.newInvocationBuilder(client,
+                String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName));
 
         val argBuilder = client.arguments();
         onArgs.accept(argBuilder);
 
-        val args = argBuilder 
+        val args = argBuilder
                 .build();
-        
+
         val argsAsJavaSource = argBuilder.getActionParameterTypes().entrySet().stream()
-        .map(entry->String.format("\n    %s %s", entry.getValue().getSimpleName(), entry.getKey()))        
+        .map(entry->String.format("\n    %s %s", entry.getValue().getSimpleName(), entry.getKey()))
         .collect(Collectors.joining(", "));
-        
+
         filter4Reporting.next(String.format("@Action\n%s %s(%s) {\n    /*...*/\n}", entityType.getSimpleName(), actionName, argsAsJavaSource));
 
         val response = request.post(args);
-        val digest = client.digest(response, entityType);
-
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
+        val digest = client.digest(response, entityType)
+                .ifFailure(Assertions::fail);
 
         return digest;
 
     }
-    
-    <T> ResponseDigest<T> digestUsingGet(String actionName, Class<T> entityType) {
+
+    <T> Try<T> digestUsingGet(final String actionName, final Class<T> entityType) {
 
         _Probe.errOut("");
         _Probe.errOut("=== %s", actionName);
         _Probe.errOut("");
-        
+
         assertTrue(restService.getPort()>0);
 
         val useRequestDebugLogging = false;
         val client = restService.newClient(useRequestDebugLogging, conversationFilters);
 
-        val request = restService.newInvocationBuilder(client, 
-                String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName)); 
+        val request = restService.newInvocationBuilder(client,
+                String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName));
 
         filter4Reporting.next(String.format("@Action\n%s %s {\n    /*...*/\n}", entityType.getSimpleName(), actionName));
 
         val response = request.get();
-        val digest = client.digest(response, entityType);
-
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
+        val digest = client.digest(response, entityType)
+                .ifFailure(Assertions::fail);
 
         return digest;
     }
 
-    <T> ResponseDigest<T> digestArray(
-            String actionName, 
-            Class<T> entityType, 
-            GenericType<List<T>> genericType) {
+    <T> Try<Can<T>> digestArray(
+            final String actionName,
+            final Class<T> entityType,
+            final GenericType<List<T>> genericType) {
         filter4Reporting.next(String.format("@Action\n%s[] %s() {\n    /*...*/\n}", entityType.getSimpleName(), actionName));
         return digestVector(actionName, entityType, genericType);
     }
-    
-    <T> ResponseDigest<T> digestList(
-            String actionName, 
-            Class<T> entityType, 
-            GenericType<List<T>> genericType) {
+
+    <T> Try<Can<T>> digestList(
+            final String actionName,
+            final Class<T> entityType,
+            final GenericType<List<T>> genericType) {
         filter4Reporting.next(String.format("@Action\nList<%s> %s() {\n    /*...*/\n}", entityType.getSimpleName(), actionName));
         return digestVector(actionName, entityType, genericType);
     }
-    
-    <T> ResponseDigest<T> digestVector(
-            String actionName, 
-            Class<T> entityType, 
-            GenericType<List<T>> genericType) {
+
+    <T> Try<Can<T>> digestVector(
+            final String actionName,
+            final Class<T> entityType,
+            final GenericType<List<T>> genericType) {
 
         _Probe.errOut("");
         _Probe.errOut("=== %s", actionName);
         _Probe.errOut("");
-        
+
         assertTrue(restService.getPort()>0);
 
         val useRequestDebugLogging = false;
         val client = restService.newClient(useRequestDebugLogging, conversationFilters);
 
-        val request = restService.newInvocationBuilder(client, 
-                String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName)); 
+        val request = restService.newInvocationBuilder(client,
+                String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName));
 
         val args = client.arguments()
                 .build();
 
         val response = request.post(args);
-        val digest = client.digestList(response, entityType, genericType);
-
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
+        val digest = client.digestList(response, entityType, genericType)
+                .ifFailure(Assertions::fail);
 
         return digest;
 
     }
-    
+
     // -- FILTER FOR REPRESENTATION TYPE
 
     static class Filter4ReprType implements ClientConversationFilter {
@@ -452,45 +443,45 @@ class RestServiceSimpifiedRepresentationTest {
         private int httpReturnCode;
 
         @Override
-        public void onRequest(String endpoint, String method, String acceptHeaderParsing,
-                Map<String, List<String>> headers, String body) {
+        public void onRequest(final String endpoint, final String method, final String acceptHeaderParsing,
+                final Map<String, List<String>> headers, final String body) {
         }
 
         @Override
-        public void onResponse(int httpReturnCode, Map<String, List<String>> headers, String body) {
-            
+        public void onResponse(final int httpReturnCode, final Map<String, List<String>> headers, final String body) {
+
             this.httpReturnCode = httpReturnCode;
-            
+
             val contentTypeHeaderStrings = Can.ofCollection(headers.get("Content-Type"));
             reprType = RepresentationTypeSimplifiedV2.parseContentTypeHeaderString(
                     contentTypeHeaderStrings.getFirst().orElse(null))
                     .orElse(null);
 //            if(reprType==null) {
 //                Assertions.fail(String.format(
-//                        "Invalid REST response, cannot parse header's Content-Type '%s' for the repr-type to use", 
+//                        "Invalid REST response, cannot parse header's Content-Type '%s' for the repr-type to use",
 //                        contentTypeHeaderStrings));
-//            } 
+//            }
         }
-        
-        void assertRepresentationType(RepresentationTypeSimplifiedV2 expected) {
+
+        void assertRepresentationType(final RepresentationTypeSimplifiedV2 expected) {
             assertEquals(expected, reprType);
         }
 
-        void assertHttpReturnCode(int expected) {
+        void assertHttpReturnCode(final int expected) {
             assertEquals(expected, httpReturnCode);
         }
-        
+
     }
-    
+
     // -- FILTER FOR ADOC REPORT
-    
+
     static class Filter4Reporting implements ClientConversationFilter {
-        
+
         private static Document doc = doc();
         private static Table table;
-        
+
         static {
-            
+
             table = table(doc);
             table.setTitle(String.format("Autogenerated by %s", RestServiceSimpifiedRepresentationTest.class.getSimpleName()));
             table.setAttribute("cols", "a,a", true);
@@ -501,22 +492,22 @@ class RestServiceSimpifiedRepresentationTest {
             cell(table, headRow, "Action");
             cell(table, headRow, "Request and Response");
         }
-        
+
         private Row row;
         private StringBuilder requestAndResponseCellContent;
 
-        void next(String javaSource) {
-            
+        void next(final String javaSource) {
+
             row = row(table);
             cell(table, row, AsciiDocFactory.toString(doc->
                 AsciiDocFactory.SourceFactory.java(doc, javaSource, null)));
             requestAndResponseCellContent = new StringBuilder();
         }
-        
+
         @Override
-        public void onRequest(String endpoint, String method, String acceptHeaderParsing,
-                Map<String, List<String>> headers, String body) {
-            
+        public void onRequest(final String endpoint, final String method, final String acceptHeaderParsing,
+                final Map<String, List<String>> headers, final String body) {
+
             requestAndResponseCellContent.append("==== REQUEST\n\n");
             requestAndResponseCellContent.append(
                     AsciiDocFactory.toString(doc->
@@ -524,27 +515,27 @@ class RestServiceSimpifiedRepresentationTest {
         }
 
         @Override
-        public void onResponse(int httpReturnCode, Map<String, List<String>> headers, String body) {
+        public void onResponse(final int httpReturnCode, final Map<String, List<String>> headers, final String body) {
             val contentTypeHeaderStrings = Can.ofCollection(headers.get("Content-Type"));
             val reprType = RepresentationTypeSimplifiedV2.parseContentTypeHeaderString(
                     contentTypeHeaderStrings.getFirst().orElse(null))
                     .orElse(null);
-            
+
             requestAndResponseCellContent
             .append("\n==== RESPONSE\n\n")
-            .append(String.format("- HTTP return code: %d\n", httpReturnCode)) 
+            .append(String.format("- HTTP return code: %d\n", httpReturnCode))
             .append(String.format("- Representation Type: %s\n\n", reprType))
             .append(AsciiDocFactory.toString(doc->
                 AsciiDocFactory.SourceFactory.json(doc, body, null)));
-            
+
             cell(table, row, requestAndResponseCellContent.toString());
         }
-        
+
         @SneakyThrows
         public static String print() {
             return AsciiDocWriter.toString(doc);
         }
-        
+
     }
 
 
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdap_restfulStressTest.java b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdap_restfulStressTest.java
index bcd8244057..921bb235be 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdap_restfulStressTest.java
+++ b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdap_restfulStressTest.java
@@ -23,6 +23,7 @@ import static java.time.Duration.ofMillis;
 import javax.inject.Inject;
 
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -34,7 +35,6 @@ import org.springframework.context.annotation.PropertySources;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTimeout;
-import static org.junit.jupiter.api.Assertions.fail;
 
 import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.core.config.presets.IsisPresets;
@@ -119,12 +119,10 @@ class ShiroSecmanLdap_restfulStressTest extends AbstractShiroTest {
         assertTimeout(ofMillis(5000), ()->{
 
             for(int i=0; i<100; ++i) {
-                val digest = restService.getHttpSessionInfo(restfulClient);
-                if(!digest.isSuccess()) {
-                    fail(digest.getFailureCause());
-                }
+                val digest = restService.getHttpSessionInfo(restfulClient)
+                        .ifFailure(Assertions::fail);
 
-                val httpSessionInfo = digest.getEntities();
+                val httpSessionInfo = digest.getValue().orElseThrow();
 
                 assertNotNull(httpSessionInfo);
 
diff --git a/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/RestServiceStressTest.java b/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/RestServiceStressTest.java
index c223fcf8e1..5baa51396d 100644
--- a/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/RestServiceStressTest.java
+++ b/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/RestServiceStressTest.java
@@ -22,6 +22,7 @@ import java.util.stream.IntStream;
 
 import javax.inject.Inject;
 
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
@@ -32,7 +33,6 @@ import org.springframework.test.context.TestPropertySource;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 
 import org.apache.isis.commons.internal.base._Timing;
 import org.apache.isis.core.config.presets.IsisPresets;
@@ -90,13 +90,10 @@ class RestServiceStressTest {
 
     void requestSingleBookOfTheWeek_viaRestEndpoint(final RestfulClient restfulClient) {
 
-        val digest = restService.getRecommendedBookOfTheWeekDto(restfulClient);
+        val digest = restService.getRecommendedBookOfTheWeekDto(restfulClient)
+                .ifFailure(Assertions::fail);
 
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
-
-        val bookOfTheWeek = digest.getEntities().getSingletonOrFail();
+        val bookOfTheWeek = digest.getValue().orElseThrow();
 
         assertNotNull(bookOfTheWeek);
         assertEquals("Book of the week", bookOfTheWeek.getName());
diff --git a/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/RestServiceTest.java b/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/RestServiceTest.java
index b4496d0ac9..07e1d71b7c 100644
--- a/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/RestServiceTest.java
+++ b/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/RestServiceTest.java
@@ -21,6 +21,7 @@ package org.apache.isis.testdomain.rest;
 import javax.inject.Inject;
 import javax.xml.bind.JAXBException;
 
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.web.server.LocalServerPort;
@@ -30,7 +31,6 @@ import org.springframework.test.context.TestPropertySource;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.testdomain.conf.Configuration_usingJdo;
@@ -61,13 +61,10 @@ class RestServiceTest {
         val useRequestDebugLogging = false;
         val restfulClient = restService.newClient(useRequestDebugLogging);
 
-        val digest = restService.getRecommendedBookOfTheWeek(restfulClient);
+        val digest = restService.getRecommendedBookOfTheWeek(restfulClient)
+                .ifFailure(Assertions::fail);
 
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
-
-        val bookOfTheWeek = digest.getEntities().getSingletonOrFail();
+        val bookOfTheWeek = digest.getValue().orElseThrow();
 
         assertNotNull(bookOfTheWeek);
         assertEquals("Book of the week", bookOfTheWeek.getName());
@@ -85,13 +82,10 @@ class RestServiceTest {
         val newBook = JdoBook.of("REST Book", "A sample REST book for testing.", 77.,
                 "REST Author", "REST ISBN", "REST Publisher");
 
-        val digest = restService.storeBook(restfulClient, newBook);
-
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
+        val digest = restService.storeBook(restfulClient, newBook)
+                .ifFailure(Assertions::fail);
 
-        val storedBook = digest.getEntities().getSingletonOrFail();
+        val storedBook = digest.getValue().orElseThrow();
 
         assertNotNull(storedBook);
         assertEquals("REST Book", storedBook.getName());
@@ -106,13 +100,10 @@ class RestServiceTest {
         val useRequestDebugLogging = false;
         val restfulClient = restService.newClient(useRequestDebugLogging);
 
-        val digest = restService.getMultipleBooks(restfulClient);
+        val digest = restService.getMultipleBooks(restfulClient)
+                .ifFailure(Assertions::fail);
 
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
-
-        val multipleBooks = digest.getEntities();
+        val multipleBooks = digest.getValue().orElseThrow();
 
         assertEquals(2, multipleBooks.size());
 
@@ -130,13 +121,10 @@ class RestServiceTest {
         val useRequestDebugLogging = false;
         val restfulClient = restService.newClient(useRequestDebugLogging);
 
-        val digest = restService.getRecommendedBookOfTheWeekAsDto(restfulClient);
-
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
+        val digest = restService.getRecommendedBookOfTheWeekAsDto(restfulClient)
+                .ifFailure(Assertions::fail);
 
-        val bookOfTheWeek = digest.getEntities().getSingletonOrFail();
+        val bookOfTheWeek = digest.getValue().orElseThrow();
 
         assertNotNull(bookOfTheWeek);
         assertEquals("Book of the week", bookOfTheWeek.getName());
@@ -151,13 +139,10 @@ class RestServiceTest {
         val useRequestDebugLogging = false;
         val restfulClient = restService.newClient(useRequestDebugLogging);
 
-        val digest = restService.getMultipleBooksAsDto(restfulClient);
+        val digest = restService.getMultipleBooksAsDto(restfulClient)
+                .ifFailure(Assertions::fail);
 
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
-
-        val multipleBooks = digest.getEntities();
+        val multipleBooks = digest.getValue().orElseThrow();
 
         assertEquals(2, multipleBooks.size());
 
@@ -167,20 +152,16 @@ class RestServiceTest {
 
     }
 
-
     @Test
     void httpSessionInfo() {
 
         val useRequestDebugLogging = false;
         val restfulClient = restService.newClient(useRequestDebugLogging);
 
-        val digest = restService.getHttpSessionInfo(restfulClient);
+        val digest = restService.getHttpSessionInfo(restfulClient)
+                .ifFailure(Assertions::fail);
 
-        if(!digest.isSuccess()) {
-            fail(digest.getFailureCause());
-        }
-
-        val httpSessionInfo = digest.getEntities().getSingletonOrFail();
+        val httpSessionInfo = digest.getValue().orElseThrow();
 
         assertNotNull(httpSessionInfo);
 
@@ -189,6 +170,4 @@ class RestServiceTest {
 
     }
 
-
-
 }
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/rest/RestEndpointService.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/rest/RestEndpointService.java
index 0e1a2c61b9..833b3e001b 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/rest/RestEndpointService.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/rest/RestEndpointService.java
@@ -30,12 +30,12 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.client.SuppressionType;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.functional.Try;
 import org.apache.isis.core.config.RestEasyConfiguration;
 import org.apache.isis.core.config.viewer.web.WebAppContextPath;
 import org.apache.isis.testdomain.jdo.entities.JdoBook;
 import org.apache.isis.testdomain.ldap.LdapConstants;
 import org.apache.isis.testdomain.util.dto.BookDto;
-import org.apache.isis.viewer.restfulobjects.client.ResponseDigest;
 import org.apache.isis.viewer.restfulobjects.client.RestfulClient;
 import org.apache.isis.viewer.restfulobjects.client.RestfulClientConfig;
 import org.apache.isis.viewer.restfulobjects.client.log.ClientConversationFilter;
@@ -117,7 +117,7 @@ public class RestEndpointService {
 
     // -- ENDPOINTS
 
-    public ResponseDigest<JdoBook> getRecommendedBookOfTheWeek(final RestfulClient client) {
+    public Try<JdoBook> getRecommendedBookOfTheWeek(final RestfulClient client) {
 
         val request = newInvocationBuilder(client, INVENTORY_RESOURCE + "/actions/recommendedBookOfTheWeek/invoke");
         val args = client.arguments()
@@ -129,7 +129,7 @@ public class RestEndpointService {
         return digest;
     }
 
-    public ResponseDigest<BookDto> getRecommendedBookOfTheWeekDto(final RestfulClient client) {
+    public Try<BookDto> getRecommendedBookOfTheWeekDto(final RestfulClient client) {
 
         val request = newInvocationBuilder(client, INVENTORY_RESOURCE + "/actions/recommendedBookOfTheWeekDto/invoke");
         val args = client.arguments()
@@ -141,7 +141,7 @@ public class RestEndpointService {
         return digest;
     }
 
-    public ResponseDigest<JdoBook> getMultipleBooks(final RestfulClient client) throws JAXBException {
+    public Try<Can<JdoBook>> getMultipleBooks(final RestfulClient client) throws JAXBException {
 
         val request = newInvocationBuilder(client, INVENTORY_RESOURCE + "/actions/multipleBooks/invoke");
         val args = client.arguments()
@@ -155,7 +155,7 @@ public class RestEndpointService {
     }
 
 
-    public ResponseDigest<JdoBook> storeBook(final RestfulClient client, final JdoBook newBook) throws JAXBException {
+    public Try<JdoBook> storeBook(final RestfulClient client, final JdoBook newBook) throws JAXBException {
 
         val request = newInvocationBuilder(client, INVENTORY_RESOURCE + "/actions/storeBook/invoke");
         val args = client.arguments()
@@ -168,7 +168,7 @@ public class RestEndpointService {
         return digest;
     }
 
-    public ResponseDigest<BookDto> getRecommendedBookOfTheWeekAsDto(final RestfulClient client) {
+    public Try<BookDto> getRecommendedBookOfTheWeekAsDto(final RestfulClient client) {
 
         val request = newInvocationBuilder(client, INVENTORY_RESOURCE + "/actions/recommendedBookOfTheWeekAsDto/invoke");
         val args = client.arguments()
@@ -180,7 +180,7 @@ public class RestEndpointService {
         return digest;
     }
 
-    public ResponseDigest<BookDto> getMultipleBooksAsDto(final RestfulClient client) throws JAXBException {
+    public Try<Can<BookDto>> getMultipleBooksAsDto(final RestfulClient client) throws JAXBException {
 
         val request = newInvocationBuilder(client, INVENTORY_RESOURCE + "/actions/multipleBooksAsDto/invoke");
         val args = client.arguments()
@@ -194,7 +194,7 @@ public class RestEndpointService {
     }
 
 
-    public ResponseDigest<String> getHttpSessionInfo(final RestfulClient client) {
+    public Try<String> getHttpSessionInfo(final RestfulClient client) {
 
         val request = newInvocationBuilder(client, INVENTORY_RESOURCE + "/actions/httpSessionInfo/invoke");
         val args = client.arguments()
diff --git a/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/ResponseDigest.java b/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/ResponseDigest.java
index c44962da23..c2201a0966 100644
--- a/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/ResponseDigest.java
+++ b/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/ResponseDigest.java
@@ -47,11 +47,8 @@ import lombok.NonNull;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
-/**
- * @since 2.0 {@index}
- */
 @Log4j2
-public class ResponseDigest<T> {
+class ResponseDigest<T> {
 
     /**
      * synchronous response processing (single entity)
@@ -113,7 +110,7 @@ public class ResponseDigest<T> {
     /**
      * @return (non-null), optionally the result if cardinality is exactly ONE
      */
-    public Optional<T> getEntity(){
+    public Optional<T> getEntity() {
         return getEntities().getSingleton();
     }
 
diff --git a/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/RestfulClient.java b/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/RestfulClient.java
index ebe9a4f96b..f9e773685f 100644
--- a/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/RestfulClient.java
+++ b/viewers/restfulobjects/client/src/main/java/org/apache/isis/viewer/restfulobjects/client/RestfulClient.java
@@ -30,6 +30,8 @@ import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.Response;
 
 import org.apache.isis.applib.client.SuppressionType;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.functional.Try;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.context._Context;
 import org.apache.isis.viewer.restfulobjects.client.auth.BasicAuthFilter;
@@ -38,6 +40,7 @@ import org.apache.isis.viewer.restfulobjects.client.log.ClientConversationLogger
 
 import static org.apache.isis.commons.internal.base._NullSafe.stream;
 
+import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
 /**
@@ -73,13 +76,13 @@ Entity<String> args = client.arguments()
 
 Response response = request.post(args);
 
-ResponseDigest<MyObject> digest = client.digest(response, MyObject.class);
+Try<MyObject> digest = client.digest(response, MyObject.class);
 }
 
 if(digest.isSuccess()) {
-    System.out.println("result: "+ digest.getEntities().getSingletonOrFail().get$$instanceId());
+    System.out.println("result: "+ digest.getValue().orElseThrow().get$$instanceId());
 } else {
-    digest.getFailureCause().printStackTrace();
+    digest.getFailure().get().printStackTrace();
 }
  * </pre></blockquote>
  *
@@ -137,12 +140,20 @@ public class RestfulClient {
 
     // -- RESPONSE PROCESSING
 
-    public <T> ResponseDigest<T> digest(final Response response, final Class<T> entityType) {
-        return ResponseDigest.wrap(response, entityType);
+    public <T> Try<T> digest(final Response response, final Class<T> entityType) {
+        val digest = ResponseDigest.wrap(response, entityType);
+        if(digest.isSuccess()) {
+            return Try.success(digest.getEntity().orElse(null));
+        }
+        return Try.failure(digest.getFailureCause());
     }
 
-    public <T> ResponseDigest<T> digestList(final Response response, final Class<T> entityType, final GenericType<List<T>> genericType) {
-        return ResponseDigest.wrapList(response, entityType, genericType);
+    public <T> Try<Can<T>> digestList(final Response response, final Class<T> entityType, final GenericType<List<T>> genericType) {
+        val listDigest = ResponseDigest.wrapList(response, entityType, genericType);
+        if(listDigest.isSuccess()) {
+            return Try.success(listDigest.getEntities());
+        }
+        return Try.failure(listDigest.getFailureCause());
     }
 
     // -- FILTER