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

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


The following commit(s) were added to refs/heads/v4 by this push:
     new 94ce09459dd CAUSEWAY-3897: more migrating RestfulClient to Spring's 
RestClient (RO)
94ce09459dd is described below

commit 94ce09459ddb64f6a493acbceb0d249d22c24104
Author: Andi Huber <[email protected]>
AuthorDate: Tue Jul 8 21:41:32 2025 +0200

    CAUSEWAY-3897: more migrating RestfulClient to Spring's RestClient (RO)
---
 .../causeway/applib/client/SuppressionType.java    |  17 +-
 .../applib/jaxb/PersistentEntityAdapter.java       |   8 +-
 regressiontests/base-jpa/pom.xml                   |   7 +-
 .../jpa/rest/JpaRestEndpointService.java           | 310 +++++++++++----------
 regressiontests/rest-jpa/pom.xml                   |  11 -
 .../testdomain/rest/jpa/RestServiceStressTest.java |  23 +-
 .../testdomain/rest/jpa/RestServiceTest.java       |  48 ++--
 .../applib/src/main/java/module-info.java          |   2 +-
 .../applib/client/ActionParameterModel.java        |   1 +
 .../applib/client/CausewayMediaTypes.java          |  79 ++++++
 .../client/ActionParameterModelRecord.java         | 214 --------------
 viewers/restfulobjects/test/pom.xml                |  24 --
 ...sewayViewerRestfulObjectsIntegTestAbstract.java |  17 --
 .../test/scenarios/Abstract_IntegTest.java         |   6 -
 ...Photo.DEPARTMENT_BOOKMARK_AS_MAP.approved.json} |   0
 ...oto.DEPARTMENT_BOOKMARK_AS_VALUE.approved.json} |   0
 ...rWithPhoto.DEPARTMENT_KEY_AS_MAP.approved.json} |   0
 ...ithPhoto.DEPARTMENT_KEY_AS_VALUE.approved.json} |   0
 ...hilevel_IntegTest.java => Staff_IntegTest.java} |   4 +-
 ...gTest.createStaffMemberWithPhoto2.approved.json |  13 -
 .../staff/Staff_lowlevel_v1_IntegTest.java         | 251 -----------------
 ...gTest.createStaffMemberWithPhoto2.approved.json |  17 --
 .../staff/Staff_lowlevel_v2_IntegTest.java         | 155 -----------
 23 files changed, 295 insertions(+), 912 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/client/SuppressionType.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/client/SuppressionType.java
index 889ded6a976..267e4d306d8 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/client/SuppressionType.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/client/SuppressionType.java
@@ -20,8 +20,13 @@
 
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.jspecify.annotations.Nullable;
 
 import org.apache.causeway.commons.internal.base._NullSafe;
+import org.apache.causeway.commons.internal.base._Strings;
 
 import static org.apache.causeway.commons.internal.base._NullSafe.stream;
 
@@ -58,11 +63,9 @@ public enum SuppressionType {
     /**
      * suppress all '$$...' entries
      */
-    ALL
-
-    ;
+    ALL;
 
-    public static EnumSet<SuppressionType> all() { return EnumSet.of(ALL); };
+    public static EnumSet<SuppressionType> all() { return EnumSet.of(ALL); }
 
     public static EnumSet<SuppressionType> setOf(final SuppressionType ... 
types){
         final EnumSet<SuppressionType> set = 
EnumSet.noneOf(SuppressionType.class);
@@ -70,6 +73,12 @@ public static EnumSet<SuppressionType> setOf(final 
SuppressionType ... types){
         return set;
     }
 
+    public static Optional<String> toLiteral(final @Nullable 
EnumSet<SuppressionType> suppressionTypes) {
+        return _Strings.nonEmpty(_NullSafe.stream(suppressionTypes)
+                .map(SuppressionType::name)
+                .collect(Collectors.joining(",")));
+    }
+
     public static class ParseUtil {
 
         public static EnumSet<SuppressionType> parse(final List<String> 
parameterList) {
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/jaxb/PersistentEntityAdapter.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/jaxb/PersistentEntityAdapter.java
index 1d67512f67d..f087a3e1645 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/jaxb/PersistentEntityAdapter.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/jaxb/PersistentEntityAdapter.java
@@ -32,15 +32,17 @@ public class PersistentEntityAdapter extends 
XmlAdapter<OidDto, Object> {
 
     @Override
     public Object unmarshal(final OidDto oidDto) throws Exception {
+        if(bookmarkService==null) return null;
+
         var bookmark = Bookmark.forOidDto(oidDto);
         return bookmarkService.lookup(bookmark).orElse(null);
     }
 
     @Override
     public OidDto marshal(final Object domainObject) throws Exception {
-        if(domainObject == null) {
-            return null;
-        }
+        if(domainObject == null) return null;
+        if(bookmarkService==null) return null;
+
         var bookmark = bookmarkService.bookmarkForElseFail(domainObject);
         return bookmark.toOidDto();
     }
diff --git a/regressiontests/base-jpa/pom.xml b/regressiontests/base-jpa/pom.xml
index ed8b478bc87..a6a10a6d095 100644
--- a/regressiontests/base-jpa/pom.xml
+++ b/regressiontests/base-jpa/pom.xml
@@ -63,7 +63,7 @@
         
         <dependency>
             <groupId>org.apache.causeway.viewer</groupId>
-            <artifactId>causeway-viewer-restfulobjects-client</artifactId>
+            <artifactId>causeway-viewer-restfulobjects-applib</artifactId>
         </dependency>
         
         <dependency>
@@ -72,11 +72,6 @@
             <type>pom</type>
             <optional>true</optional>
         </dependency>
-<!--        <dependency>-->
-<!--            <groupId>org.apache.causeway.viewer</groupId>-->
-<!--            
<artifactId>causeway-viewer-restfulobjects-jaxrsresteasy</artifactId>-->
-<!--            <optional>true</optional>-->
-<!--        </dependency>-->
         <dependency>
             <groupId>org.apache.causeway.viewer</groupId>
             <artifactId>causeway-viewer-wicket-viewer</artifactId>
diff --git 
a/regressiontests/base-jpa/src/main/java/org/apache/causeway/testdomain/jpa/rest/JpaRestEndpointService.java
 
b/regressiontests/base-jpa/src/main/java/org/apache/causeway/testdomain/jpa/rest/JpaRestEndpointService.java
index 430b974a4ee..c3784a15de7 100644
--- 
a/regressiontests/base-jpa/src/main/java/org/apache/causeway/testdomain/jpa/rest/JpaRestEndpointService.java
+++ 
b/regressiontests/base-jpa/src/main/java/org/apache/causeway/testdomain/jpa/rest/JpaRestEndpointService.java
@@ -18,39 +18,55 @@
  */
 package org.apache.causeway.testdomain.jpa.rest;
 
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
-
 import jakarta.inject.Inject;
-import jakarta.ws.rs.client.Invocation;
-import jakarta.ws.rs.core.GenericType;
 import jakarta.xml.bind.JAXBException;
 
-import org.jspecify.annotations.NonNull;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+
+import org.jspecify.annotations.Nullable;
+import org.slf4j.Logger;
 
+import org.springframework.core.ParameterizedTypeReference;
 import org.springframework.core.env.Environment;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.client.RestClient.Builder;
+import org.springframework.web.client.RestClient.RequestBodySpec;
+import org.springframework.web.client.RestClient.RequestBodyUriSpec;
 
-import org.apache.causeway.applib.client.SuppressionType;
 import org.apache.causeway.applib.services.iactnlayer.InteractionService;
+import org.apache.causeway.applib.value.semantics.ValueDecomposition;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.functional.Try;
+import org.apache.causeway.commons.internal.base._Bytes;
+import org.apache.causeway.commons.io.JsonUtils;
 import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.core.config.applib.RestfulPathProvider;
 import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
 import org.apache.causeway.extensions.fullcalendar.applib.value.CalendarEvent;
 import 
org.apache.causeway.extensions.fullcalendar.applib.value.CalendarEventSemantics;
+import org.apache.causeway.schema.common.v2.ValueType;
 import org.apache.causeway.testdomain.jpa.JpaInventoryJaxbVm;
 import org.apache.causeway.testdomain.jpa.JpaTestFixtures;
 import org.apache.causeway.testdomain.jpa.entities.JpaBook;
 import org.apache.causeway.testdomain.ldap.LdapConstants;
 import org.apache.causeway.testdomain.util.dto.BookDto;
-import org.apache.causeway.viewer.restfulobjects.client.AuthenticationMode;
-import org.apache.causeway.viewer.restfulobjects.client.RestfulClient;
-import org.apache.causeway.viewer.restfulobjects.client.RestfulClientConfig;
-import org.apache.causeway.viewer.restfulobjects.client.RestfulClientMediaType;
-import 
org.apache.causeway.viewer.restfulobjects.client.log.ClientConversationFilter;
+import 
org.apache.causeway.viewer.restfulobjects.applib.client.ActionParameterModel;
+import 
org.apache.causeway.viewer.restfulobjects.applib.client.CausewayMediaTypes;
+import 
org.apache.causeway.viewer.restfulobjects.applib.client.ConversationLogger;
 
+import lombok.Getter;
 import lombok.RequiredArgsConstructor;
+import lombok.experimental.Accessors;
 import lombok.extern.slf4j.Slf4j;
 
 @Service
@@ -75,191 +91,195 @@ public int getPort() {
 
     // -- NEW CLIENT
 
-    public RestfulClient newClient(final boolean useRequestDebugLogging) {
-        return newClient(useRequestDebugLogging, Can.empty());
-    }
+    @Getter(lazy = true) @Accessors(fluent=true)
+    private final String baseUrl = "http://0.0.0.0:%d%s/".formatted(getPort(), 
webAppContextPath
+            .prependContextPath(new 
RestfulPathProvider(causewayConfiguration).getRestfulPath().orElse("")));
 
-    public RestfulClient newClient(
-            final boolean useRequestDebugLogging,
-            final @NonNull Can<ClientConversationFilter> additionalFilters) {
-
-        var restfulPathProvider = new 
RestfulPathProvider(causewayConfiguration);
-
-        var restRootPath =
-                String.format("http://localhost:%d%s/";,
-                        getPort(),
-                        webAppContextPath
-                            
.prependContextPath(restfulPathProvider.getRestfulPath().orElse(""))
-                );
-
-        log.debug("new restful client created for {}", restRootPath);
-
-        var clientConfig = RestfulClientConfig.builder()
-                .restfulBaseUrl(restRootPath)
-                // setup basic-auth
-                .authenticationMode(AuthenticationMode.BASIC)
-                .basicAuthUser(LdapConstants.SVEN_PRINCIPAL)
-                .basicAuthPassword("pass")
-                // setup request/response debug logging
-                .useRequestDebugLogging(useRequestDebugLogging)
-                // register additional filter if any
-                .clientConversationFilters(additionalFilters.toList())
-                .build();
-
-        var client = RestfulClient.ofConfig(clientConfig);
-        return client;
+    record ValueHolder(String type, Object value) {
+        ValueDecomposition parseValueDecomposition() {
+            return ValueDecomposition.destringify(ValueType.COMPOSITE, 
(String)value);
+        }
+        @SuppressWarnings("unchecked")
+        <T> T value(final Class<T> requiredType){
+            return (T) value;
+        }
     }
+    record CausewayMessageConverter() implements HttpMessageConverter<Object> {
 
-    // -- NEW REQUEST BUILDER
+        @Override
+        public boolean canRead(final Class<?> clazz, @Nullable final MediaType 
mediaType) {
+            return clazz.equals(ValueDecomposition.class);
+        }
 
-    public Invocation.Builder newInvocationBuilder(final RestfulClient client, 
final String endpointPath) {
-        var accept = 
RestfulClientMediaType.SIMPLE_JSON.mediaTypeFor(Object.class, 
SuppressionType.all()).toString();
-        return client.request(endpointPath)
-                .accept(accept);
-    }
+        @Override
+        public boolean canWrite(final Class<?> clazz, @Nullable final 
MediaType mediaType) {
+            return false;
+        }
 
-    // -- ENDPOINTS
+        @Override
+        public List<MediaType> getSupportedMediaTypes() {
+            return List.of(MediaType.APPLICATION_JSON);
+        }
 
-    public Try<JpaBook> getRecommendedBookOfTheWeek(final RestfulClient 
client) {
+        @Override
+        public Object read(final Class<? extends Object> clazz, final 
HttpInputMessage inputMessage)
+            throws IOException, HttpMessageNotReadableException {
+            var bytes = _Bytes.of(inputMessage.getBody());
+            var json = new String(bytes, StandardCharsets.UTF_8);
+            var valueHolder = JsonUtils.tryRead(ValueHolder.class, json)
+                .valueAsNonNullElseFail();
+            return ValueDecomposition.destringify(ValueType.COMPOSITE, 
valueHolder.value(String.class));
+        }
 
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + 
"/actions/recommendedBookOfTheWeek/invoke");
-        var args = client.arguments()
-                .build();
+        @Override
+        public void write(final Object t, @Nullable final MediaType 
contentType, final HttpOutputMessage outputMessage)
+            throws IOException, HttpMessageNotWritableException {
+            // TODO Auto-generated method stub
 
-        var response = request.post(args);
-        var digest = client.digest(response, JpaBook.class);
+        }
 
-        return digest;
     }
 
-    public Try<BookDto> getRecommendedBookOfTheWeekDto(final RestfulClient 
client) {
+    protected Builder restClient() {
+        return RestClient.builder()
+            .messageConverters(converters->converters.add(0, new 
CausewayMessageConverter()))
+            .baseUrl(baseUrl())
+            .defaultHeaders(headers -> 
headers.setBasicAuth(LdapConstants.SVEN_PRINCIPAL, "pass"));
+    }
+    protected Builder restClient(final Logger logger) {
+        return restClient()
+            .bufferContent((uri, method)->true)
+            .requestInterceptor(new ConversationLogger(msg->logger.info(msg)));
+    }
+    protected ActionParameterModel actParamModel() {
+        return ActionParameterModel.create(baseUrl());
+    }
 
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + 
"/actions/recommendedBookOfTheWeekDto/invoke");
-        var args = client.arguments()
-                .build();
+    public RestClient newClient(final boolean useRequestDebugLogging) {
+        log.debug("new restful client created for {}", baseUrl());
+        return useRequestDebugLogging
+            ? restClient(log).build()
+            : restClient().build();
+    }
 
-        var response = request.post(args);
-        var digest = client.digest(response, BookDto.class);
+    // -- NEW REQUEST BUILDER
 
-        return digest;
+    public RequestBodySpec request(final RequestBodyUriSpec 
requestBodyUriSpec, final String uri,
+        final ActionParameterModel actParamModel) {
+        return requestBodyUriSpec
+            .uri(INVENTORY_RESOURCE + uri)
+            .accept(CausewayMediaTypes.CAUSEWAY_JSON_V2_LIGHT)
+            .body(actParamModel.toJson());
     }
 
-    public Try<Can<JpaBook>> getMultipleBooks(final RestfulClient client) 
throws JAXBException {
+    // -- ENDPOINTS
 
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + "/actions/multipleBooks/invoke");
-        var args = client.arguments()
-                .addActionParameter("nrOfBooks", 3)
-                .build();
+    public Try<JpaBook> getRecommendedBookOfTheWeek(final RestClient client) {
 
-        var response = request.post(args);
-        var digest = client.digestList(response, JpaBook.class, new 
GenericType<List<JpaBook>>() {});
+        var response = request(client.post(), 
"/actions/recommendedBookOfTheWeek/invoke", actParamModel())
+            .retrieve();
 
-        return digest;
+        var entity = response.body(JpaBook.class);
+        return Try.success(entity);
     }
 
-    public Try<JpaBook> storeBook(final RestfulClient client, final JpaBook 
newBook) throws JAXBException {
-
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + "/actions/storeBook/invoke");
-        var args = client.arguments()
-                .addActionParameter("newBook", BookDto.from(newBook).encode())
-                .build();
-
-        var response = request.post(args);
-        var digest = client.digest(response, JpaBook.class);
+    public Try<BookDto> getRecommendedBookOfTheWeekDto(final RestClient 
client) {
+        var response = request(client.post(), 
"/actions/recommendedBookOfTheWeekDto/invoke", actParamModel())
+            .retrieve();
 
-        return digest;
+        var entity = response.body(BookDto.class);
+        return Try.success(entity);
     }
 
-    public Try<BookDto> getRecommendedBookOfTheWeekAsDto(final RestfulClient 
client) {
+    public Try<Can<JpaBook>> getMultipleBooks(final RestClient client) throws 
JAXBException {
+        var response = request(client.post(),"/actions/multipleBooks/invoke", 
actParamModel()
+                .addActionParameter("nrOfBooks", 3))
+            .retrieve();
 
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + 
"/actions/recommendedBookOfTheWeekAsDto/invoke");
-        var args = client.arguments()
-                .build();
+        List<JpaBook> books = response
+            .body(new ParameterizedTypeReference<List<JpaBook>>() {});
 
-        var response = request.post(args);
-        var digest = client.digest(response, BookDto.class);
-
-        return digest;
+        return Try.success(Can.ofCollection(books));
     }
 
-    public Try<Can<BookDto>> getMultipleBooksAsDto(final RestfulClient client) 
throws JAXBException {
+    public Try<JpaBook> storeBook(final RestClient client, final JpaBook 
newBook) throws JAXBException {
+        var response = request(client.post(), "/actions/storeBook/invoke", 
actParamModel()
+            .addActionParameter("newBook", BookDto.from(newBook).encode()))
+            .retrieve();
 
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + "/actions/multipleBooksAsDto/invoke");
-        var args = client.arguments()
-                .addActionParameter("nrOfBooks", 2)
-                .build();
+        var entity = response.body(JpaBook.class);
+        return Try.success(entity);
+    }
 
-        var response = request.post(args);
-        var digest = client.digestList(response, BookDto.class, new 
GenericType<List<BookDto>>() {});
+    public Try<BookDto> getRecommendedBookOfTheWeekAsDto(final RestClient 
client) {
+        var response = request(client.post(), 
"/actions/recommendedBookOfTheWeekAsDto/invoke", actParamModel())
+            .retrieve();
 
-        return digest;
+        var entity = response.body(BookDto.class);
+        return Try.success(entity);
     }
 
-    public Try<JpaInventoryJaxbVm> getInventoryAsJaxbVm(final RestfulClient 
client) {
+    public Try<Can<BookDto>> getMultipleBooksAsDto(final RestClient client) 
throws JAXBException {
+        var response = request(client.post(), 
"/actions/multipleBooksAsDto/invoke", actParamModel()
+            .addActionParameter("nrOfBooks", 2))
+            .retrieve();
 
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + "/actions/inventoryAsJaxbVm/invoke");
-        var args = client.arguments()
-                .build();
+        List<BookDto> books = response
+            .body(new ParameterizedTypeReference<List<BookDto>>() {});
 
-        var response = request.post(args);
-        var digest = client.digest(response, JpaInventoryJaxbVm.class);
-        return digest;
+        return Try.success(Can.ofCollection(books));
     }
 
-    public Try<Can<JpaBook>> getBooksFromInventoryAsJaxbVm(final RestfulClient 
client) {
+    public Try<JpaInventoryJaxbVm> getInventoryAsJaxbVm(final RestClient 
client) {
+        var response = request(client.post(), 
"/actions/inventoryAsJaxbVm/invoke", actParamModel())
+            .retrieve();
+
+        return JsonUtils.tryRead(JpaInventoryJaxbVm.class, 
response.body(String.class),
+            JsonUtils::jaxbAnnotationSupport,
+            m->m.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES),
+            m->m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+         //var entity = response.body(JpaInventoryJaxbVm.class);
+        //return Try.success(entity);
+    }
 
+    public Try<Can<JpaBook>> getBooksFromInventoryAsJaxbVm(final RestClient 
client) {
         var objectId = interactionService.callAnonymous(
                 
()->jpaTestFixtures.getInventoryJaxbVmAsBookmark().identifier());
 
         // using domain object alias ...
-        var request = newInvocationBuilder(client,
-                "objects/testdomain.jpa.JpaInventoryJaxbVmAlias/"
-                        + objectId + "/actions/listBooks/invoke");
-
-        var args = client.arguments()
-                .build();
-
-        var response = request.post(args);
-        var digest = client.digestList(response, JpaBook.class, new 
GenericType<List<JpaBook>>() {});
-
-        return digest;
+        var response = client
+            .post()
+            
.uri("objects/testdomain.jpa.JpaInventoryJaxbVmAlias/%s/actions/listBooks/invoke"
+                .formatted(objectId))
+            .accept(CausewayMediaTypes.CAUSEWAY_JSON_V2_LIGHT)
+            .body(actParamModel().toJson())
+            .retrieve();
+
+        List<JpaBook> books = response
+            .body(new ParameterizedTypeReference<List<JpaBook>>() {});
+
+        return Try.success(Can.ofCollection(books));
     }
 
     public Try<CalendarEvent> echoCalendarEvent(
-            final RestfulClient client, final CalendarEvent calendarEvent) {
-
+            final RestClient client, final CalendarEvent calendarEvent) {
         var calSemantics = new CalendarEventSemantics();
+        var response = 
request(client.post(),"/actions/echoCalendarEvent/invoke", actParamModel()
+            .addActionParameter("calendarEvent", 
calSemantics.decompose(calendarEvent)))
+            .retrieve();
+        var entity = response.body(ValueDecomposition.class);
 
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + "/actions/echoCalendarEvent/invoke");
-        var args = client.arguments()
-                .addActionParameter("calendarEvent", 
calSemantics.decompose(calendarEvent))
-                .build();
-
-        var response = request.post(args);
-        var digest = client.digestValue(response, calSemantics);
-
-        return digest;
+        var calendarEventEcho = calSemantics.compose(entity);
+        return Try.success(calendarEventEcho);
     }
 
-    public Try<String> getHttpSessionInfo(final RestfulClient client) {
-
-        var request = newInvocationBuilder(client,
-                INVENTORY_RESOURCE + "/actions/httpSessionInfo/invoke");
-        var args = client.arguments()
-                .build();
-
-        var response = request.post(args);
-        var digest = client.digest(response, String.class);
+    public Try<String> getHttpSessionInfo(final RestClient client) {
+        var args = actParamModel();
+        var response = request(client.post(), 
"/actions/httpSessionInfo/invoke", args)
+            .retrieve();
 
-        return digest;
+        var entity = response.body(ValueHolder.class);
+        return Try.success((String)entity.value());
     }
 
     // -- HELPER
diff --git a/regressiontests/rest-jpa/pom.xml b/regressiontests/rest-jpa/pom.xml
index 389463f6bf1..5ab1fe8b5df 100644
--- a/regressiontests/rest-jpa/pom.xml
+++ b/regressiontests/rest-jpa/pom.xml
@@ -57,22 +57,11 @@
                        <scope>test</scope>
                </dependency>
 
-               <dependency>
-                       <groupId>org.apache.causeway.viewer</groupId>
-                       
<artifactId>causeway-viewer-restfulobjects-client</artifactId>
-                       <scope>test</scope>
-               </dependency>
-
                <dependency>
                        <groupId>org.apache.causeway.mavendeps</groupId>
                        <artifactId>causeway-mavendeps-webapp</artifactId>
                        <type>pom</type>
                </dependency>
-<!--           <dependency>-->
-<!--                   <groupId>org.apache.causeway.viewer</groupId>-->
-<!--                   
<artifactId>causeway-viewer-restfulobjects-jaxrsresteasy</artifactId>-->
-<!--                   <scope>test</scope>-->
-<!--           </dependency>-->
 
                <dependency>
             <groupId>org.apache.causeway.extensions</groupId>
diff --git 
a/regressiontests/rest-jpa/src/test/java/org/apache/causeway/testdomain/rest/jpa/RestServiceStressTest.java
 
b/regressiontests/rest-jpa/src/test/java/org/apache/causeway/testdomain/rest/jpa/RestServiceStressTest.java
index e59d12e4dd6..6ca2cc89635 100644
--- 
a/regressiontests/rest-jpa/src/test/java/org/apache/causeway/testdomain/rest/jpa/RestServiceStressTest.java
+++ 
b/regressiontests/rest-jpa/src/test/java/org/apache/causeway/testdomain/rest/jpa/RestServiceStressTest.java
@@ -22,7 +22,6 @@
 
 import jakarta.inject.Inject;
 
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -32,12 +31,12 @@
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.web.server.test.LocalServerPort;
 import org.springframework.context.annotation.Import;
+import org.springframework.web.client.RestClient;
 
 import org.apache.causeway.commons.internal.base._Timing;
 import org.apache.causeway.testdomain.jpa.conf.Configuration_usingJpa;
 import org.apache.causeway.testdomain.jpa.rest.JpaRestEndpointService;
 import 
org.apache.causeway.testing.unittestsupport.applib.annotations.DisabledIfRunningWithSurefire;
-import org.apache.causeway.viewer.restfulobjects.client.RestfulClient;
 import 
org.apache.causeway.viewer.restfulobjects.viewer.CausewayModuleViewerRestfulObjectsViewer;
 
 import lombok.extern.slf4j.Slf4j;
@@ -80,17 +79,12 @@ void bookOfTheWeek_stressTest() {
 
             IntStream.range(0, clients)
             //.parallel()
-            .mapToObj(i->{
-                var restfulClient = 
restService.newClient(USE_REQUEST_DEBUG_LOGGING);
-                return restfulClient;
-            })
-            .forEach(restfulClient->{
-
+            .mapToObj(i->restService.newClient(USE_REQUEST_DEBUG_LOGGING))
+            .forEach(restClient->{
                 IntStream.range(0, iterations)
                 .forEach(iter->{
-                    requestSingleBookOfTheWeek_viaRestEndpoint(restfulClient);
+                    requestSingleBookOfTheWeek_viaRestEndpoint(restClient);
                 });
-
             });
 
         });
@@ -99,12 +93,9 @@ void bookOfTheWeek_stressTest() {
 
     }
 
-    void requestSingleBookOfTheWeek_viaRestEndpoint(final RestfulClient 
_restfulClient) {
-        var restfulClient = restService.newClient(USE_REQUEST_DEBUG_LOGGING);
-        var digest = 
restService.getRecommendedBookOfTheWeekAsDto(restfulClient)
-                .ifFailure(Assertions::fail);
-
-        var bookOfTheWeek = digest.getValue().orElseThrow();
+    void requestSingleBookOfTheWeek_viaRestEndpoint(final RestClient 
restClient) {
+        var bookOfTheWeek = 
restService.getRecommendedBookOfTheWeekAsDto(restClient)
+            .valueAsNonNullElseFail();
 
         assertNotNull(bookOfTheWeek);
         assertEquals("Book of the week", bookOfTheWeek.getName());
diff --git 
a/regressiontests/rest-jpa/src/test/java/org/apache/causeway/testdomain/rest/jpa/RestServiceTest.java
 
b/regressiontests/rest-jpa/src/test/java/org/apache/causeway/testdomain/rest/jpa/RestServiceTest.java
index 383b3ab9279..8cc7849c0fd 100644
--- 
a/regressiontests/rest-jpa/src/test/java/org/apache/causeway/testdomain/rest/jpa/RestServiceTest.java
+++ 
b/regressiontests/rest-jpa/src/test/java/org/apache/causeway/testdomain/rest/jpa/RestServiceTest.java
@@ -35,6 +35,7 @@
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.web.server.test.LocalServerPort;
 import org.springframework.context.annotation.Import;
+import org.springframework.web.client.RestClient;
 
 import 
org.apache.causeway.extensions.fullcalendar.applib.value.CalendarEventSemantics;
 import org.apache.causeway.testdomain.jpa.JpaInventoryJaxbVm;
@@ -43,7 +44,6 @@
 import org.apache.causeway.testdomain.jpa.conf.Configuration_usingJpa;
 import org.apache.causeway.testdomain.jpa.entities.JpaBook;
 import org.apache.causeway.testdomain.jpa.rest.JpaRestEndpointService;
-import org.apache.causeway.viewer.restfulobjects.client.RestfulClient;
 import 
org.apache.causeway.viewer.restfulobjects.viewer.CausewayModuleViewerRestfulObjectsViewer;
 
 @SpringBootTest(
@@ -66,22 +66,18 @@ class RestServiceTest extends RegressionTestWithJpaFixtures 
{
     @LocalServerPort int port; // just for reference (not used)
     @Inject JpaRestEndpointService restService;
 
-    private RestfulClient restfulClient;
+    private RestClient restClient;
 
     @BeforeEach
     void checkPrereq() {
         assertTrue(restService.getPort()>0);
-        this.restfulClient = restService.newClient(USE_REQUEST_DEBUG_LOGGING);
+        this.restClient = restService.newClient(USE_REQUEST_DEBUG_LOGGING);
     }
 
     @Test @Order(1)
     void httpSessionInfo() {
-        var digest = restService.getHttpSessionInfo(restfulClient)
-                .ifFailureFail();
-
-        var httpSessionInfo = digest.getValue().orElseThrow();
-
-        assertNotNull(httpSessionInfo);
+        var httpSessionInfo = restService.getHttpSessionInfo(restClient)
+                .valueAsNonNullElseFail();
 
         // NB: this works only because we excluded wicket viewer from the app.
         assertEquals("no http-session", httpSessionInfo);
@@ -89,10 +85,10 @@ void httpSessionInfo() {
 
     @Test @Order(2)
     void bookOfTheWeek_viaRestEndpoint() {
-        var digest = restService.getRecommendedBookOfTheWeek(restfulClient)
+        var entity = restService.getRecommendedBookOfTheWeek(restClient)
                 .ifFailureFail();
 
-        var bookOfTheWeek = digest.getValue().orElseThrow();
+        var bookOfTheWeek = entity.valueAsNonNullElseFail();
 
         assertNotNull(bookOfTheWeek);
         assertEquals("Book of the week", bookOfTheWeek.getName());
@@ -103,10 +99,10 @@ void addNewBook_viaRestEndpoint() throws JAXBException {
         var newBook = JpaBook.of("REST Book", "A sample REST book for 
testing.", 77.,
                 "REST Author", "REST ISBN", "REST Publisher");
 
-        var digest = restService.storeBook(restfulClient, newBook)
+        var entity = restService.storeBook(restClient, newBook)
                 .ifFailureFail();
 
-        var storedBook = digest.getValue().orElseThrow();
+        var storedBook = entity.valueAsNonNullElseFail();
 
         assertNotNull(storedBook);
         assertEquals("REST Book", storedBook.getName());
@@ -114,12 +110,12 @@ void addNewBook_viaRestEndpoint() throws JAXBException {
 
     @Test @Order(4)
     void multipleBooks_viaRestEndpoint() throws JAXBException {
-        var digest = restService.getMultipleBooks(restfulClient)
+        var entity = restService.getMultipleBooks(restClient)
                 .ifFailureFail();
 
         var expectedBookTitles = JpaTestFixtures.expectedBookTitles();
 
-        var multipleBooks = digest.getValue().orElseThrow()
+        var multipleBooks = entity.valueAsNonNullElseFail()
                 .filter(book->expectedBookTitles.contains(book.getName()));
 
         assertEquals(3, multipleBooks.size());
@@ -127,10 +123,10 @@ void multipleBooks_viaRestEndpoint() throws JAXBException 
{
 
     @Test @Order(5)
     void bookOfTheWeek_asDto_viaRestEndpoint() {
-        var digest = 
restService.getRecommendedBookOfTheWeekAsDto(restfulClient)
+        var entity = restService.getRecommendedBookOfTheWeekAsDto(restClient)
                 .ifFailureFail();
 
-        var bookOfTheWeek = digest.getValue().orElseThrow();
+        var bookOfTheWeek = entity.valueAsNonNullElseFail();
 
         assertNotNull(bookOfTheWeek);
         assertEquals("Book of the week", bookOfTheWeek.getName());
@@ -138,10 +134,10 @@ void bookOfTheWeek_asDto_viaRestEndpoint() {
 
     @Test @Order(6)
     void multipleBooks_asDto_viaRestEndpoint() throws JAXBException {
-        var digest = restService.getMultipleBooksAsDto(restfulClient)
+        var entity = restService.getMultipleBooksAsDto(restClient)
                 .ifFailureFail();
 
-        var multipleBooks = digest.getValue().orElseThrow();
+        var multipleBooks = entity.valueAsNonNullElseFail();
 
         assertEquals(2, multipleBooks.size());
 
@@ -152,10 +148,8 @@ void multipleBooks_asDto_viaRestEndpoint() throws 
JAXBException {
 
     @Test @Order(7)
     void inventoryAsJaxbVm_viaRestEndpoint() {
-        var digest = restService.getInventoryAsJaxbVm(restfulClient)
-                .ifFailureFail();
-
-        final JpaInventoryJaxbVm inventoryAsJaxbVm = 
digest.getValue().orElseThrow();
+        final JpaInventoryJaxbVm inventoryAsJaxbVm = 
restService.getInventoryAsJaxbVm(restClient)
+                .valueAsNonNullElseFail();
 
         assertNotNull(inventoryAsJaxbVm);
         assertEquals("Bookstore", inventoryAsJaxbVm.getName());
@@ -163,10 +157,10 @@ void inventoryAsJaxbVm_viaRestEndpoint() {
 
     @Test @Order(8)
     void listBooks_fromInventoryAsJaxbVm_viaRestEndpoint() {
-        var digest = restService.getBooksFromInventoryAsJaxbVm(restfulClient)
+        var entity = restService.getBooksFromInventoryAsJaxbVm(restClient)
                 .ifFailure(Assertions::fail);
 
-        var books = digest.getValue().orElseThrow();
+        var books = entity.valueAsNonNullElseFail();
 
         var expectedBookTitles = JpaTestFixtures.expectedBookTitles();
 
@@ -192,10 +186,10 @@ void calendarEvent_echo_viaRestEndpoint() {
          * "cardinality":4
          * }
          */
-        var digest = restService.echoCalendarEvent(restfulClient, calSample)
+        var entity = restService.echoCalendarEvent(restClient, calSample)
                 .ifFailure(Assertions::fail);
 
-        var calSampleEchoed = digest.getValue().orElseThrow();
+        var calSampleEchoed = entity.valueAsNonNullElseFail();
         assertEquals(calSample, calSampleEchoed);
     }
 
diff --git a/viewers/restfulobjects/applib/src/main/java/module-info.java 
b/viewers/restfulobjects/applib/src/main/java/module-info.java
index ccc5b76f788..a4d25379a87 100644
--- a/viewers/restfulobjects/applib/src/main/java/module-info.java
+++ b/viewers/restfulobjects/applib/src/main/java/module-info.java
@@ -36,7 +36,7 @@
     requires com.fasterxml.jackson.annotation;
     requires com.fasterxml.jackson.core;
     requires com.fasterxml.jackson.databind;
-    requires org.apache.causeway.applib;
+    requires transitive org.apache.causeway.applib;
     requires org.apache.causeway.commons;
     requires spring.context;
     requires spring.core;
diff --git 
a/viewers/restfulobjects/applib/src/main/java/org/apache/causeway/viewer/restfulobjects/applib/client/ActionParameterModel.java
 
b/viewers/restfulobjects/applib/src/main/java/org/apache/causeway/viewer/restfulobjects/applib/client/ActionParameterModel.java
index d55adafe628..bb4c68103c5 100644
--- 
a/viewers/restfulobjects/applib/src/main/java/org/apache/causeway/viewer/restfulobjects/applib/client/ActionParameterModel.java
+++ 
b/viewers/restfulobjects/applib/src/main/java/org/apache/causeway/viewer/restfulobjects/applib/client/ActionParameterModel.java
@@ -65,4 +65,5 @@ static ActionParameterModel create(String baseUrl) {
     }
 
     String toJson();
+
 }
\ No newline at end of file
diff --git 
a/viewers/restfulobjects/applib/src/main/java/org/apache/causeway/viewer/restfulobjects/applib/client/CausewayMediaTypes.java
 
b/viewers/restfulobjects/applib/src/main/java/org/apache/causeway/viewer/restfulobjects/applib/client/CausewayMediaTypes.java
new file mode 100644
index 00000000000..50afbe40e1c
--- /dev/null
+++ 
b/viewers/restfulobjects/applib/src/main/java/org/apache/causeway/viewer/restfulobjects/applib/client/CausewayMediaTypes.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.viewer.restfulobjects.applib.client;
+
+import java.util.EnumSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.http.MediaType;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.LinkedCaseInsensitiveMap;
+import org.springframework.util.StringUtils;
+
+import org.apache.causeway.applib.client.SuppressionType;
+import org.apache.causeway.commons.internal.base._Strings;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class CausewayMediaTypes {
+
+    public final MediaType CAUSEWAY_XML = profile(MediaType.APPLICATION_XML, 
"urn:org.restfulobjects:repr-types/action-result");
+    public final MediaType CAUSEWAY_JSON_V2 = 
profile(MediaType.APPLICATION_JSON, "urn:org.apache.causeway/v2");
+    public final MediaType CAUSEWAY_JSON_V2_LIGHT = suppress(CAUSEWAY_JSON_V2, 
SuppressionType.all());
+
+    public MediaType appendParameter(MediaType input, String name, String 
value) {
+        if(!StringUtils.hasLength(name)) return input;
+        value = _Strings.nullToEmpty(value);
+
+        var parameters = input.getParameters();
+        if (!CollectionUtils.isEmpty(parameters)) {
+            var map = new LinkedCaseInsensitiveMap<String>(parameters.size(), 
Locale.ROOT);
+            map.put(name, value);
+            parameters.forEach(map::put);
+            return new MediaType(input, map);
+        }
+        return new MediaType(input, Map.of(name, value));
+    }
+
+    public MediaType profile(MediaType input, String urn) {
+        if(!StringUtils.hasLength(urn)) return input;
+
+        urn = _Strings.prefix(urn, "\"");
+        urn = _Strings.suffix(urn, "\"");
+
+        return appendParameter(input, "profile", urn);
+    }
+
+    public MediaType suppress(MediaType input, EnumSet<SuppressionType> 
suppressionTypes) {
+        return SuppressionType.toLiteral(suppressionTypes)
+            .map(value->appendParameter(input, "suppress", value))
+            .orElse(input);
+    }
+
+    public MediaType domainType(MediaType input, Class<?> domainType) {
+        return Optional.ofNullable(domainType)
+            .map(Class::getName)
+            .map(name->appendParameter(input, "x-ro-domain-type", name))
+            .orElse(input);
+    }
+
+}
diff --git 
a/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/ActionParameterModelRecord.java
 
b/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/ActionParameterModelRecord.java
deleted file mode 100644
index 10ceb669f72..00000000000
--- 
a/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/ActionParameterModelRecord.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.viewer.restfulobjects.client;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import jakarta.ws.rs.client.Entity;
-
-import org.apache.causeway.applib.services.bookmark.Bookmark;
-
-import org.jspecify.annotations.Nullable;
-
-import org.apache.causeway.applib.util.schema.CommonDtoUtils;
-import org.apache.causeway.applib.value.Blob;
-import org.apache.causeway.applib.value.Clob;
-import org.apache.causeway.applib.value.semantics.ValueDecomposition;
-import org.apache.causeway.commons.io.JsonUtils;
-import org.apache.causeway.schema.common.v2.BlobDto;
-import org.apache.causeway.schema.common.v2.ClobDto;
-import org.apache.causeway.schema.common.v2.ValueType;
-import org.apache.causeway.schema.common.v2.ValueWithTypeDto;
-
-import lombok.Getter;
-import org.jspecify.annotations.NonNull;
-
-/**
- * Use {@link RestfulClient#arguments()} to get an instance.
- * @since 2.0 {@index}
- */
-public class ActionParameterModelRecord {
-
-    private final Map<String, String> actionParameters = new LinkedHashMap<>();
-
-    @Getter
-    private final Map<String, Class<?>> actionParameterTypes = new 
LinkedHashMap<>();
-
-    private final RestfulClient restfulClient;
-
-    public ActionParameterModelRecord(RestfulClient restfulClient) {
-        this.restfulClient = restfulClient;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final String parameterValue) {
-        actionParameters.put(parameterName, parameterValue != null
-                ? value("\"" + parameterValue + "\"")
-                : value(JSON_NULL_LITERAL));
-        actionParameterTypes.put(parameterName, String.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final int parameterValue) {
-        actionParameters.put(parameterName, value(""+parameterValue));
-        actionParameterTypes.put(parameterName, int.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final long parameterValue) {
-        actionParameters.put(parameterName, value(""+parameterValue));
-        actionParameterTypes.put(parameterName, long.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final byte parameterValue) {
-        actionParameters.put(parameterName, value(""+parameterValue));
-        actionParameterTypes.put(parameterName, byte.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final short parameterValue) {
-        actionParameters.put(parameterName, value(""+parameterValue));
-        actionParameterTypes.put(parameterName, short.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final double parameterValue) {
-        actionParameters.put(parameterName, value(""+parameterValue));
-        actionParameterTypes.put(parameterName, double.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final float parameterValue) {
-        actionParameters.put(parameterName, value(""+parameterValue));
-        actionParameterTypes.put(parameterName, float.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final boolean parameterValue) {
-        actionParameters.put(parameterName, value(""+parameterValue));
-        actionParameterTypes.put(parameterName, boolean.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final Blob blob) {
-        var blobDto = new BlobDto();
-        blobDto.setName(blob.name());
-        blobDto.setMimeType(blob.mimeType().getBaseType());
-        blobDto.setBytes(blob.bytes());
-        var fundamentalTypeDto = new ValueWithTypeDto();
-        fundamentalTypeDto.setType(ValueType.BLOB);
-        fundamentalTypeDto.setBlob(blobDto);
-        actionParameters.put(parameterName, 
value(CommonDtoUtils.getFundamentalValueAsJson(fundamentalTypeDto)));
-        actionParameterTypes.put(parameterName, Blob.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final Clob clob) {
-        var clobDto = new ClobDto();
-        clobDto.setName(clob.name());
-        clobDto.setMimeType(clob.mimeType().getBaseType());
-        clobDto.setChars(clob.asString());
-        var fundamentalTypeDto = new ValueWithTypeDto();
-        fundamentalTypeDto.setType(ValueType.CLOB);
-        fundamentalTypeDto.setClob(clobDto);
-        actionParameters.put(parameterName, 
value(CommonDtoUtils.getFundamentalValueAsJson(fundamentalTypeDto)));
-        actionParameterTypes.put(parameterName, Blob.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName,
-            final @NonNull Map<String, Object> map) {
-        var nestedJson = JsonUtils.toStringUtf8(map);
-        actionParameters.put(parameterName, value(nestedJson));
-        actionParameterTypes.put(parameterName, Map.class);
-        return this;
-    }
-
-    public ActionParameterModelRecord addActionParameter(
-            final @NonNull String parameterName,
-            final @NonNull Bookmark bookmark) {
-        if (this.restfulClient == null) {
-            throw new IllegalStateException("Use RestfulClient#arguments() to 
create this builder");
-        }
-        actionParameters.put(parameterName, valueHref( bookmark) );
-        actionParameterTypes.put(parameterName, Map.class);
-        return this;
-    }
-
-    private String valueHref(Bookmark bookmark) {
-        String hrefValue  = asAbsoluteHref(bookmark);
-//        String hrefValue  = "\"" + asAbsoluteHref(bookmark) + "\"";
-        Map<String, String> map = Map.of("href", hrefValue);
-        return value(JsonUtils.toStringUtf8(map));
-    }
-
-    private String asAbsoluteHref(Bookmark bookmark) {
-        return String.format("%s%s", 
restfulClient.getConfig().getRestfulBaseUrl(), asRelativeHref(bookmark));
-    }
-
-    private String asRelativeHref(Bookmark bookmark) {
-        return String.format("objects/%s/%s", bookmark.logicalTypeName(), 
bookmark.identifier());
-    }
-
-    public <T> ActionParameterModelRecord addActionParameter(
-            final String parameterName,
-            final @NonNull Class<T> type,
-            final @Nullable T object) {
-        var nestedJson = object!=null
-            ? JsonUtils.toStringUtf8(object)
-            : "NULL"; // see ValueSerializerDefault.ENCODED_NULL
-        actionParameters.put(parameterName, value(nestedJson));
-        actionParameterTypes.put(parameterName, type);
-        return this;
-    }
-
-    /**
-     * For transport of {@link ValueDecomposition} over REST.
-     * @see RestfulClient#digestValue(jakarta.ws.rs.core.Response, 
org.apache.causeway.applib.value.semantics.ValueSemanticsProvider)
-     */
-    public ActionParameterModelRecord addActionParameter(final String 
parameterName, final ValueDecomposition decomposition) {
-        return addActionParameter(parameterName, decomposition.stringify());
-    }
-
-    public Entity<String> build() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("{\n")
-        .append(actionParameters.entrySet().stream()
-                .map(this::toJson)
-                .collect(Collectors.joining(",\n")))
-        .append("\n}");
-
-        return Entity.json(sb.toString());
-    }
-
-    // -- HELPER
-
-    private static final String JSON_NULL_LITERAL = "null";
-
-    private String value(final String valueLiteral) {
-        return "{\"value\" : " + valueLiteral + "}";
-    }
-
-    private String toJson(final Map.Entry<String, String> entry) {
-        return "   \""+entry.getKey()+"\": "+entry.getValue();
-    }
-
-}
diff --git a/viewers/restfulobjects/test/pom.xml 
b/viewers/restfulobjects/test/pom.xml
index cb2bfdff45d..a2ffa1f159f 100644
--- a/viewers/restfulobjects/test/pom.xml
+++ b/viewers/restfulobjects/test/pom.xml
@@ -41,19 +41,6 @@
                <maven.deploy.skip>true</maven.deploy.skip>
        </properties>
 
-       <build>
-               <testResources>
-                       <testResource>
-                               <directory>src/test/resources</directory>
-                               <filtering>true</filtering>
-                       </testResource>
-                       <testResource>
-                               <directory>src/test/java</directory>
-                               <filtering>false</filtering>
-                       </testResource>
-               </testResources>
-       </build>
-
        <dependencies>
 
                <!-- TESTING -->
@@ -107,11 +94,6 @@
                        
<artifactId>causeway-testing-fixtures-applib</artifactId>
                        <scope>test</scope>
                </dependency>
-               <dependency>
-                       <groupId>org.apache.causeway.viewer</groupId>
-                       
<artifactId>causeway-viewer-restfulobjects-client</artifactId>
-                       <scope>test</scope>
-               </dependency>
                <dependency>
                        <groupId>org.apache.causeway.viewer</groupId>
                        
<artifactId>causeway-viewer-restfulobjects-viewer</artifactId>
@@ -162,11 +144,5 @@
                        <scope>test</scope>
                </dependency>
 
-               <dependency>
-                       <groupId>com.google.code.gson</groupId>
-                       <artifactId>gson</artifactId>
-                       <scope>test</scope>
-               </dependency>
-
        </dependencies>
 </project>
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/CausewayViewerRestfulObjectsIntegTestAbstract.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/CausewayViewerRestfulObjectsIntegTestAbstract.java
index b9bc60f26b5..94c02e1dd2a 100644
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/CausewayViewerRestfulObjectsIntegTestAbstract.java
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/CausewayViewerRestfulObjectsIntegTestAbstract.java
@@ -58,10 +58,6 @@
 import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
 import 
org.apache.causeway.viewer.restfulobjects.applib.client.ConversationLogger;
-import org.apache.causeway.viewer.restfulobjects.client.AuthenticationMode;
-import org.apache.causeway.viewer.restfulobjects.client.RestfulClient;
-import org.apache.causeway.viewer.restfulobjects.client.RestfulClientConfig;
-
 import static 
org.apache.causeway.commons.internal.assertions._Assert.assertNotNull;
 
 import lombok.SneakyThrows;
@@ -173,19 +169,6 @@ public void handleError(final URI url, final HttpMethod 
method, final ClientHttp
         };
     }
 
-    @Deprecated
-    protected RestfulClient restfulClient() {
-        var clientConfig = RestfulClientConfig.builder()
-                .restfulBaseUrl(String.format("http://0.0.0.0:%d/restful/";, 
port))
-                .authenticationMode(AuthenticationMode.BASIC)
-                .basicAuthUser("any")           // using bypass auth.
-                .basicAuthPassword("any")
-                .useRequestDebugLogging(true) // default = false
-                .build();
-
-        return RestfulClient.ofConfig(clientConfig);
-    }
-
     public enum BookmarkOptions {
         SCRUB,
         PRESERVE
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/Abstract_IntegTest.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/Abstract_IntegTest.java
index 1cb87a5ad41..90827c8c3df 100644
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/Abstract_IntegTest.java
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/Abstract_IntegTest.java
@@ -34,7 +34,6 @@
 import org.springframework.transaction.annotation.Propagation;
 import org.apache.causeway.applib.services.bookmark.BookmarkService;
 import 
org.apache.causeway.persistence.jpa.eclipselink.CausewayModulePersistenceJpaEclipselink;
-import org.apache.causeway.viewer.restfulobjects.client.RestfulClient;
 import 
org.apache.causeway.viewer.restfulobjects.test.CausewayViewerRestfulObjectsIntegTestAbstract;
 import org.apache.causeway.viewer.restfulobjects.test.domain.UniversityModule;
 import org.apache.causeway.viewer.restfulobjects.test.domain.dom.Department;
@@ -57,8 +56,6 @@ public abstract class Abstract_IntegTest extends 
CausewayViewerRestfulObjectsInt
     @Inject protected StaffMemberRepository staffMemberRepository;
     @Inject protected BookmarkService bookmarkService;
 
-    @Deprecated protected RestfulClient restfulClient;
-
     protected Abstract_IntegTest(final Class<?> resourceBaseClazz) {
         super(resourceBaseClazz);
     }
@@ -96,14 +93,11 @@ protected void beforeEach(){
 
         });
 
-        restfulClient = restfulClient();
     }
 
     @Override
     @AfterEach
     protected void afterEach(){
-        restfulClient.close();
-
         transactionService.runTransactional(Propagation.REQUIRED, () -> {
             staffMemberRepository.removeAll();
             deptHeadRepository.findAll().forEach(x -> x.setDepartment(null));
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_BOOKMARK_AS_MAP.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_BOOKMARK_AS_MAP.approved.json
similarity index 100%
rename from 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_BOOKMARK_AS_MAP.approved.json
rename to 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_BOOKMARK_AS_MAP.approved.json
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_BOOKMARK_AS_VALUE.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_BOOKMARK_AS_VALUE.approved.json
similarity index 100%
rename from 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_BOOKMARK_AS_VALUE.approved.json
rename to 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_BOOKMARK_AS_VALUE.approved.json
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_KEY_AS_MAP.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_KEY_AS_MAP.approved.json
similarity index 100%
rename from 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_KEY_AS_MAP.approved.json
rename to 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_KEY_AS_MAP.approved.json
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_KEY_AS_VALUE.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_KEY_AS_VALUE.approved.json
similarity index 100%
rename from 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_KEY_AS_VALUE.approved.json
rename to 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.createStaffMemberWithPhoto.DEPARTMENT_KEY_AS_VALUE.approved.json
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.java
similarity index 97%
rename from 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.java
rename to 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.java
index 94f01274006..f65e86f5a2a 100644
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.java
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.java
@@ -45,7 +45,7 @@
 import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
-class Staff_hilevel_IntegTest extends Abstract_IntegTest {
+class Staff_IntegTest extends Abstract_IntegTest {
 
     final String staffName = "Fred Smith";
 
@@ -153,7 +153,7 @@ private Blob readFileAsBlob(final String fileName) {
     }
 
     private String asAbsoluteHref(final Bookmark bookmark) {
-        return String.format("%s%s", 
restfulClient.getConfig().getRestfulBaseUrl(), asRelativeHref(bookmark));
+        return String.format("%s%s", baseUrl(), asRelativeHref(bookmark));
     }
 
     private String asRelativeHref(final Bookmark bookmark) {
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.createStaffMemberWithPhoto2.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.createStaffMemberWithPhoto2.approved.json
deleted file mode 100644
index 5c1d9667211..00000000000
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.createStaffMemberWithPhoto2.approved.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "name" : {
-    "value" : "Fred Smith"
-  },
-  "department" : {
-    "value" : {
-      "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.Department/NNN";
-    }
-  },
-  "photo" : {
-    "value" : 
"StaffMember-photo-Bar.pdf:application/pdf:JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFuZyhlbi1HQikgL1N0cnVjdFRyZWVSb290IDEyIDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4vTWV0YWRhdGEgMzIgMCBSL1ZpZXdlclByZWZlcmVuY2VzIDMzIDAgUj4+DQplbmRvYmoNCjIgMCBvYmoNCjw8L1R5cGUvUGFnZXMvQ291bnQgMS9LaWRzWyAzIDAgUl0gPj4NCmVuZG9iag0KMyAwIG9iag0KPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjEgNSAwIFIvRjIgOSAwIFI+Pi9FeHRHU3RhdGU8PC9HUzcgNyAwIFIvR1M4IDggMCB
 [...]
-  }
-}
\ No newline at end of file
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.java
deleted file mode 100644
index d634d95e942..00000000000
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.viewer.restfulobjects.test.scenarios.staff;
-
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.util.Base64;
-
-import jakarta.activation.MimeType;
-import jakarta.activation.MimeTypeParseException;
-import jakarta.inject.Named;
-import jakarta.ws.rs.client.Entity;
-import jakarta.ws.rs.client.Invocation;
-import jakarta.ws.rs.core.Response;
-
-import com.google.gson.GsonBuilder;
-
-import org.approvaltests.Approvals;
-import org.approvaltests.reporters.DiffReporter;
-import org.approvaltests.reporters.UseReporter;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-import org.springframework.context.annotation.Import;
-import org.springframework.stereotype.Component;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.annotation.DirtiesContext.MethodMode;
-import org.springframework.transaction.annotation.Propagation;
-
-import org.apache.causeway.applib.services.bookmark.Bookmark;
-import org.apache.causeway.applib.value.Blob;
-import org.apache.causeway.applib.value.NamedWithMimeType;
-import org.apache.causeway.applib.value.semantics.Renderer;
-import org.apache.causeway.applib.value.semantics.ValueDecomposition;
-import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
-import org.apache.causeway.commons.collections.Can;
-import org.apache.causeway.commons.internal.base._Bytes;
-import org.apache.causeway.commons.internal.base._Strings;
-import org.apache.causeway.commons.io.DataSource;
-import org.apache.causeway.core.metamodel.valuesemantics.BlobValueSemantics;
-import org.apache.causeway.schema.common.v2.ValueType;
-import 
org.apache.causeway.viewer.restfulobjects.test.scenarios.Abstract_IntegTest;
-
-import lombok.Getter;
-import lombok.SneakyThrows;
-
-@Import({
-    Staff_lowlevel_v1_IntegTest.BlobValueSemanticsV1LegacyEncoding.class
-})
-class Staff_lowlevel_v1_IntegTest extends Abstract_IntegTest {
-
-    @Test
-    @UseReporter(DiffReporter.class)
-    @DirtiesContext(methodMode = MethodMode.AFTER_METHOD)
-    @SneakyThrows
-    void createStaffMemberWithPhoto2() {
-
-        // given
-        final var staffName = "Fred Smith";
-
-        final var bookmarkBeforeIfAny = 
transactionService.callTransactional(Propagation.REQUIRED, () -> {
-            final var staffMember = 
staffMemberRepository.findByName(staffName);
-            return bookmarkService.bookmarkFor(staffMember);
-        }).valueAsNonNullElseFail();
-
-        assertThat(bookmarkBeforeIfAny).isEmpty();
-
-        // and given
-        final var departmentName = "Classics";
-        final var departmentBookmark = 
transactionService.callTransactional(Propagation.REQUIRED, () -> {
-            final var staffMember = 
departmentRepository.findByName(departmentName);
-            return bookmarkService.bookmarkFor(staffMember).orElseThrow();
-        }).valueAsNonNullElseFail();
-
-        String departmentHref = asRelativeHref(departmentBookmark);
-        Invocation.Builder departmentRequest = 
restfulClient.request(departmentHref);
-        Response departmentResponse = departmentRequest.get();
-        
assertThat(departmentResponse.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
-
-        // and given
-        final var photoEncoded = 
readFileAndEncodeAsBlob("StaffMember-photo-Bar.pdf");
-
-        // when create request
-        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto2/invoke");
-
-        final var body = new Body(staffName, 
asAbsoluteHref(departmentBookmark), photoEncoded);
-        final var bodyJson = new GsonBuilder().create().toJson(body);
-
-        // then
-        Approvals.verify(bodyJson, jsonOptions());
-
-        // and when send request
-        var response = requestBuilder.post(Entity.entity(bodyJson, 
"application/json"));
-
-        // then
-        assertResponseOK(response);
-        var entity = response.readEntity(String.class);
-        assertNotNull(entity);
-
-        // and also object is created in database
-        final var bookmarkAfterIfAny = 
transactionService.callTransactional(Propagation.REQUIRED, () -> {
-            final var staffMember = 
staffMemberRepository.findByName(staffName);
-            return bookmarkService.bookmarkFor(staffMember);
-        }).valueAsNonNullElseFail();
-        assertThat(bookmarkAfterIfAny).isNotEmpty();
-    }
-
-    private String asAbsoluteHref(final Bookmark bookmark) {
-        return String.format("%s%s", 
restfulClient.getConfig().getRestfulBaseUrl(), asRelativeHref(bookmark));
-    }
-
-    private String asRelativeHref(final Bookmark bookmark) {
-        return String.format("objects/%s/%s", bookmark.logicalTypeName(), 
bookmark.identifier());
-    }
-
-    private String readFileAndEncodeAsBlob(final String fileName) throws 
IOException, URISyntaxException {
-        var bytes = DataSource.ofResource(Abstract_IntegTest.class, fileName)
-                .bytes();
-        String photoEncoded = encodePdf(fileName, bytes);
-        return photoEncoded;
-    }
-
-    private String encodePdf(final String fileName, final byte[] pdfBytes) 
throws URISyntaxException {
-        final String pdfBytesEncoded = 
Base64.getEncoder().encodeToString(pdfBytes);
-        final String encodedBlob = String.format("%s:%s:%s", fileName, 
"application/pdf", pdfBytesEncoded);
-        return encodedBlob;
-    }
-
-    @Getter
-    static class Body {
-
-        private Name name;
-        private Department department;
-        private Blob photo;
-
-        /**
-         * @param nameValue
-         * @param departmentHrefValue
-         * @param blobValue - is the Blob encoded format: 
"filename.pdf:application/pdf:pdfBytesBase64Encoded"
-         */
-        Body(final String nameValue, final String departmentHrefValue, final 
String blobValue) {
-            photo = new Blob(blobValue);
-            name = new Name(nameValue);
-            department = new Department(new 
Department.Value(departmentHrefValue));
-        }
-
-        record Name(String value) {
-        }
-
-        record Department(Value value) {
-            record Value(String href) {
-            }
-        }
-
-        record Blob(String value) {
-        }
-    }
-
-    @Component
-    @Named("causeway.metamodel.value.BlobValueSemanticsV1LegacyEncoding")   // 
must have different name to original
-    static class BlobValueSemanticsV1LegacyEncoding
-        extends BlobValueSemantics
-        implements Renderer<Blob> {
-
-        public BlobValueSemanticsV1LegacyEncoding() {
-        }
-
-        @Override
-        public Class<Blob> getCorrespondingClass() {
-            return Blob.class;
-        }
-
-        @Override
-        public ValueType getSchemaValueType() {
-            return ValueType.STRING;
-        }
-
-        // -- COMPOSER
-
-        @Override
-        public ValueDecomposition decompose(final Blob value) {
-            return decomposeAsString(value, this::toEncodedString, () -> null);
-        }
-
-        @Override
-        public Blob compose(final ValueDecomposition decomposition) {
-            return composeFromString(decomposition, this::fromEncodedString, 
()->null);
-        }
-
-        // RENDERER
-
-        @Override
-        public String titlePresentation(final ValueSemanticsProvider.Context 
context, final Blob value) {
-            return renderTitle(value, Blob::name);
-        }
-
-        @Override
-        public String htmlPresentation(final ValueSemanticsProvider.Context 
context, final Blob value) {
-            return renderHtml(value, Blob::name);
-        }
-
-        private String toEncodedString(final Blob blob) {
-            return blob.name() + ":" + blob.mimeType().getBaseType() + ":" +
-            _Strings.ofBytes(_Bytes.encodeToBase64(Base64.getEncoder(), 
blob.bytes()), StandardCharsets.UTF_8);
-        }
-
-        private Blob fromEncodedString(final String data) {
-            final int colonIdx = data.indexOf(':');
-            final String name  = data.substring(0, colonIdx);
-            final int colon2Idx  = data.indexOf(":", colonIdx+1);
-            final String mimeTypeBase = data.substring(colonIdx+1, colon2Idx);
-            final String payload = data.substring(colon2Idx+1);
-            final byte[] bytes = _Bytes.decodeBase64(Base64.getDecoder(), 
payload.getBytes(StandardCharsets.UTF_8));
-            try {
-                return new Blob(name, new MimeType(mimeTypeBase), bytes);
-            } catch (MimeTypeParseException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        // -- EXAMPLES
-
-        @Override
-        public Can<Blob> getExamples() {
-            return Can.of(
-                    Blob.of("a Blob", NamedWithMimeType.CommonMimeType.BIN, 
new byte[] {1, 2, 3}),
-                    Blob.of("another Blob", 
NamedWithMimeType.CommonMimeType.BIN, new byte[] {3, 4}));
-        }
-
-    }
-
-}
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.createStaffMemberWithPhoto2.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.createStaffMemberWithPhoto2.approved.json
deleted file mode 100644
index a6123f9fb94..00000000000
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.createStaffMemberWithPhoto2.approved.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "name" : {
-    "value" : "Fred Smith"
-  },
-  "department" : {
-    "value" : {
-      "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.Department/NNN";
-    }
-  },
-  "photo" : {
-    "value" : {
-      "name" : "StaffMember-photo-Bar.pdf",
-      "mimeType" : "application/pdf",
-      "bytes" : 
"JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFuZyhlbi1HQikgL1N0cnVjdFRyZWVSb290IDEyIDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4vTWV0YWRhdGEgMzIgMCBSL1ZpZXdlclByZWZlcmVuY2VzIDMzIDAgUj4+DQplbmRvYmoNCjIgMCBvYmoNCjw8L1R5cGUvUGFnZXMvQ291bnQgMS9LaWRzWyAzIDAgUl0gPj4NCmVuZG9iag0KMyAwIG9iag0KPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjEgNSAwIFIvRjIgOSAwIFI+Pi9FeHRHU3RhdGU8PC9HUzcgNyAwIFIvR1M4IDggMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1hZ2VCL0l
 [...]
-    }
-  }
-}
\ No newline at end of file
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.java
deleted file mode 100644
index e5ee41e8fe6..00000000000
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.viewer.restfulobjects.test.scenarios.staff;
-
-import java.io.IOException;
-import java.util.Base64;
-
-import jakarta.ws.rs.client.Entity;
-import jakarta.ws.rs.client.Invocation;
-import jakarta.ws.rs.core.Response;
-
-import com.google.gson.GsonBuilder;
-
-import org.approvaltests.Approvals;
-import org.approvaltests.reporters.DiffReporter;
-import org.approvaltests.reporters.UseReporter;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-import org.springframework.transaction.annotation.Propagation;
-
-import org.apache.causeway.applib.services.bookmark.Bookmark;
-import org.apache.causeway.applib.value.Blob;
-import org.apache.causeway.commons.io.DataSource;
-import 
org.apache.causeway.viewer.restfulobjects.test.scenarios.Abstract_IntegTest;
-
-import lombok.Getter;
-import lombok.SneakyThrows;
-
-class Staff_lowlevel_v2_IntegTest extends Abstract_IntegTest {
-
-    @Test
-    @UseReporter(DiffReporter.class)
-    @SneakyThrows
-    public void createStaffMemberWithPhoto2() {
-
-        // given
-        final var staffName = "Fred Smith";
-
-        final var bookmarkBeforeIfAny = 
transactionService.callTransactional(Propagation.REQUIRED, () -> {
-            final var staffMember = 
staffMemberRepository.findByName(staffName);
-            return bookmarkService.bookmarkFor(staffMember);
-        }).valueAsNonNullElseFail();
-
-        assertThat(bookmarkBeforeIfAny).isEmpty();
-
-        // and given
-        final var departmentName = "Classics";
-        final var departmentBookmark = 
transactionService.callTransactional(Propagation.REQUIRED, () -> {
-            final var staffMember = 
departmentRepository.findByName(departmentName);
-            return bookmarkService.bookmarkFor(staffMember).orElseThrow();
-        }).valueAsNonNullElseFail();
-
-        String departmentHref = asRelativeHref(departmentBookmark);
-        Invocation.Builder departmentRequest = 
restfulClient.request(departmentHref);
-        Response departmentResponse = departmentRequest.get();
-        
assertThat(departmentResponse.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
-
-        // and given
-        final var photoEncoded = readFileAsBlob("StaffMember-photo-Bar.pdf");
-
-        // when create request
-        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto2/invoke");
-
-        final var body = new Body(staffName, 
asAbsoluteHref(departmentBookmark), photoEncoded);
-        final var bodyJson = new GsonBuilder().create().toJson(body);
-
-        // then
-        Approvals.verify(bodyJson, jsonOptions());
-
-        // and when send request
-        var response = requestBuilder.post(Entity.entity(bodyJson, 
"application/json"));
-
-        // then
-        assertResponseOK(response);
-        var entity = response.readEntity(String.class);
-        assertNotNull(entity);
-
-        // and also object is created in database
-        final var bookmarkAfterIfAny = 
transactionService.callTransactional(Propagation.REQUIRED, () -> {
-            final var staffMember = 
staffMemberRepository.findByName(staffName);
-            return bookmarkService.bookmarkFor(staffMember);
-        }).valueAsNonNullElseFail();
-        assertThat(bookmarkAfterIfAny).isNotEmpty();
-    }
-
-    private String asAbsoluteHref(final Bookmark bookmark) {
-        return String.format("%s%s", 
restfulClient.getConfig().getRestfulBaseUrl(), asRelativeHref(bookmark));
-    }
-
-    private String asRelativeHref(final Bookmark bookmark) {
-        return String.format("objects/%s/%s", bookmark.logicalTypeName(), 
bookmark.identifier());
-    }
-
-    private Blob readFileAsBlob(final String fileName) throws IOException {
-        var bytes = DataSource.ofResource(Abstract_IntegTest.class, fileName)
-                .bytes();
-        return new Blob(fileName, "application/pdf", bytes);
-    }
-
-    @Getter
-    static class Body {
-
-        private Name name;
-        private Department department;
-        private Blob photo;
-
-        /**
-         * @param nameValue
-         * @param departmentHrefValue
-         * @param blob - is the Blob to be formatted
-         */
-        Body(final String nameValue, final String departmentHrefValue, final 
org.apache.causeway.applib.value.Blob blob) {
-            name = new Name(nameValue);
-            department = new Department(new 
Department.Value(departmentHrefValue));
-            photo = new Blob(new Blob.Value(blob.name(), 
blob.mimeType().toString(), Base64.getEncoder().encodeToString(blob.bytes())));
-        }
-
-        record Name(String value) {
-        }
-
-        record Department(Value value) {
-            record Value(String href) {
-            }
-        }
-
-        record Blob(Value value) {
-            record Value(
-                    String name,
-                    String mimeType,
-                    String bytes) {
-            }
-        }
-
-    }
-
-}

Reply via email to