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

danhaywood pushed a commit to branch CAUSEWAY-3718
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit bdbef1770b9833d2368af77de1e2d47ed09adf5d
Author: danhaywood <[email protected]>
AuthorDate: Sat Apr 6 12:52:41 2024 +0100

    CAUSEWAY-3718: reworks and extens integ tests for REST API (hilevel and 
lolevel)
---
 .../client/ActionParameterListBuilder.java         |  45 +++-
 .../restfulobjects/client/RestfulClient.java       |   2 +-
 .../restfulobjects/test/domain/dom/Staff.java      |  11 +
 ...IntegTest.can_create_staff_member.approved.json | 109 --------
 .../test/scenarios/staff/Staff_IntegTest.java      | 102 --------
 ...egTest.createStaffMemberWithPhoto.approved.json |  17 ++
 ...gTest.createStaffMemberWithPhoto2.approved.json |  17 ++
 ...teStaffMemberWithPhoto2_using_map.approved.json |  17 ++
 ...ateStaffMemberWithPhoto_using_map.approved.json |  17 ++
 .../scenarios/staff/Staff_hilevel_IntegTest.java   | 278 +++++++++++++++++++++
 ...gTest.createStaffMemberWithPhoto2.approved.json |  13 +
 ...gTest.java => Staff_lowlevel_v1_IntegTest.java} |  57 +++--
 ...gTest.createStaffMemberWithPhoto2.approved.json |  17 ++
 ...gTest.java => Staff_lowlevel_v2_IntegTest.java} |  83 +++---
 14 files changed, 507 insertions(+), 278 deletions(-)

diff --git 
a/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/ActionParameterListBuilder.java
 
b/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/ActionParameterListBuilder.java
index a2223b4d21..21b69bb098 100644
--- 
a/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/ActionParameterListBuilder.java
+++ 
b/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/ActionParameterListBuilder.java
@@ -24,6 +24,8 @@ import java.util.stream.Collectors;
 
 import javax.ws.rs.client.Entity;
 
+import org.apache.causeway.applib.services.bookmark.Bookmark;
+
 import org.springframework.lang.Nullable;
 
 import org.apache.causeway.applib.util.schema.CommonDtoUtils;
@@ -49,6 +51,19 @@ public class ActionParameterListBuilder {
     @Getter
     private final Map<String, Class<?>> actionParameterTypes = new 
LinkedHashMap<>();
 
+    private final RestfulClient restfulClient;
+
+    /**
+     * @deprecated  - use {@link RestfulClient#arguments()}
+     */
+    @Deprecated
+    public ActionParameterListBuilder() {
+        this(null);
+    }
+    public ActionParameterListBuilder(RestfulClient restfulClient) {
+        this.restfulClient = restfulClient;
+    }
+
     public ActionParameterListBuilder addActionParameter(final String 
parameterName, final String parameterValue) {
         actionParameters.put(parameterName, parameterValue != null
                 ? value("\"" + parameterValue + "\"")
@@ -133,7 +148,35 @@ public class ActionParameterListBuilder {
         return this;
     }
 
-    public <T> ActionParameterListBuilder addActionParameter(final String 
parameterName,
+    public ActionParameterListBuilder 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.getLogicalTypeName(), 
bookmark.getIdentifier());
+    }
+
+
+    public <T> ActionParameterListBuilder addActionParameter(
+            final String parameterName,
             final @NonNull Class<T> type,
             final @Nullable T object) {
         var nestedJson = object!=null
diff --git 
a/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/RestfulClient.java
 
b/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/RestfulClient.java
index 97134fbe57..7c7f3cf60d 100644
--- 
a/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/RestfulClient.java
+++ 
b/viewers/restfulobjects/client/src/main/java/org/apache/causeway/viewer/restfulobjects/client/RestfulClient.java
@@ -172,7 +172,7 @@ public class RestfulClient implements AutoCloseable {
     // -- ARGUMENT BUILDER
 
     public ActionParameterListBuilder arguments() {
-        return new ActionParameterListBuilder();
+        return new ActionParameterListBuilder(this);
     }
 
     // -- RESPONSE PROCESSING
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/domain/dom/Staff.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/domain/dom/Staff.java
index 794cdb3c17..37d394023a 100644
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/domain/dom/Staff.java
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/domain/dom/Staff.java
@@ -60,6 +60,17 @@ public class Staff {
         return staffMember;
     }
 
+    @Action(semantics = SemanticsOf.NON_IDEMPOTENT)
+    public StaffMember createStaffMemberWithPhoto2(
+            final String name,
+            final Department department,
+            final Blob photo
+    ){
+        final var staffMember = createStaffMember(name, department);
+        staffMember.setPhoto(photo);
+        return staffMember;
+    }
+
     @Action(semantics = SemanticsOf.SAFE)
     public List<StaffMember> findAllStaffMembers(){
         return staffMemberRepository.findAll();
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.can_create_staff_member.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.can_create_staff_member.approved.json
deleted file mode 100644
index d5143d4cd6..0000000000
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.can_create_staff_member.approved.json
+++ /dev/null
@@ -1,109 +0,0 @@
-{
-  "links" : [ {
-    "rel" : "self",
-    "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.StaffMember/NNN";,
-    "method" : "GET",
-    "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/object\"",
-    "title" : "Untitled Staff Member"
-  }, {
-    "rel" : "describedby",
-    "href" : 
"http://0.0.0.0:NNN/restful/domain-types/university.dept.StaffMember";,
-    "method" : "GET",
-    "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/domain-type\""
-  }, {
-    "rel" : "urn:org.apache.causeway.restfulobjects:rels/object-layout",
-    "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.StaffMember/NNN/object-layout";,
-    "method" : "GET",
-    "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/object-layout-bs\""
-  }, {
-    "rel" : "urn:org.apache.causeway.restfulobjects:rels/object-icon",
-    "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.StaffMember/NNN/object-icon";,
-    "method" : "GET",
-    "type" : "image/*"
-  }, {
-    "rel" : "urn:org.restfulobjects:rels/update",
-    "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.StaffMember:NNN";,
-    "method" : "PUT",
-    "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/object\"",
-    "arguments" : { }
-  } ],
-  "extensions" : {
-    "oid" : "university.dept.StaffMember:NNN",
-    "isService" : false,
-    "isPersistent" : true
-  },
-  "title" : "Untitled Staff Member",
-  "domainType" : "university.dept.StaffMember",
-  "instanceId" : "NNN",
-  "members" : {
-    "department" : {
-      "id" : "department",
-      "memberType" : "property",
-      "links" : [ {
-        "rel" : "urn:org.restfulobjects:rels/details;property=\"department\"",
-        "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.StaffMember/NNN/properties/department";,
-        "method" : "GET",
-        "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
-      } ],
-      "value" : {
-        "rel" : "urn:org.restfulobjects:rels/value",
-        "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.Department/NNN";,
-        "method" : "GET",
-        "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/object\"",
-        "title" : "Untitled Department"
-      },
-      "disabledReason" : "Disabled via @DomainObject annotation, reason not 
given."
-    },
-    "grade" : {
-      "id" : "grade",
-      "memberType" : "property",
-      "links" : [ {
-        "rel" : "urn:org.restfulobjects:rels/details;property=\"grade\"",
-        "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.StaffMember/NNN/properties/grade";,
-        "method" : "GET",
-        "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
-      } ],
-      "value" : {
-        "enumType" : 
"org.apache.causeway.viewer.restfulobjects.test.domain.dom.Grade",
-        "enumName" : "LECTURER",
-        "enumTitle" : "Lecturer"
-      },
-      "extensions" : {
-        "x-causeway-format" : "enum"
-      }
-    },
-    "name" : {
-      "id" : "name",
-      "memberType" : "property",
-      "links" : [ {
-        "rel" : "urn:org.restfulobjects:rels/details;property=\"name\"",
-        "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.StaffMember/NNN/properties/name";,
-        "method" : "GET",
-        "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
-      } ],
-      "value" : "Fred Smith",
-      "extensions" : {
-        "x-causeway-format" : "string"
-      }
-    },
-    "photo" : {
-      "id" : "photo",
-      "memberType" : "property",
-      "links" : [ {
-        "rel" : "urn:org.restfulobjects:rels/details;property=\"photo\"",
-        "href" : 
"http://0.0.0.0:NNN/restful/objects/university.dept.StaffMember/NNN/properties/photo";,
-        "method" : "GET",
-        "type" : 
"application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
-      } ],
-      "value" : {
-        "name" : "StaffMember-photo-Bar.pdf",
-        "mimeType" : "application/pdf",
-        "bytes" : 
"JVBERi0xLjcNCiW1tbW1DQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvTGFuZyhlbi1HQikgL1N0cnVjdFRyZWVSb290IDEyIDAgUi9NYXJrSW5mbzw8L01hcmtlZCB0cnVlPj4vTWV0YWRhdGEgMzIgMCBSL1ZpZXdlclByZWZlcmVuY2VzIDMzIDAgUj4+DQplbmRvYmoNCjIgMCBvYmoNCjw8L1R5cGUvUGFnZXMvQ291bnQgMS9LaWRzWyAzIDAgUl0gPj4NCmVuZG9iag0KMyAwIG9iag0KPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjEgNSAwIFIvRjIgOSAwIFI+Pi9FeHRHU3RhdGU8PC9HUzcgNyAwIFIvR1M4IDggMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1hZ2VCL
 [...]
-      },
-      "extensions" : {
-        "x-causeway-format" : "blob"
-      },
-      "disabledReason" : "Disabled via @DomainObject annotation, reason not 
given."
-    }
-  }
-}
\ No newline at end of file
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.java
deleted file mode 100644
index 316fdb2e1e..0000000000
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_IntegTest.java
+++ /dev/null
@@ -1,102 +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 javax.ws.rs.core.Response;
-
-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 org.springframework.transaction.annotation.Propagation;
-
-import org.apache.causeway.applib.value.Blob;
-import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
-import org.apache.causeway.commons.io.DataSource;
-import org.apache.causeway.viewer.restfulobjects.test.domain.dom.Department;
-import 
org.apache.causeway.viewer.restfulobjects.test.scenarios.Abstract_IntegTest;
-
-import lombok.SneakyThrows;
-import lombok.val;
-
-class Staff_IntegTest extends Abstract_IntegTest {
-
-    @SneakyThrows
-    @Test
-    @UseReporter(DiffReporter.class)
-    public void can_create_staff_member() {
-
-        // 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();
-
-        final Blob photo = readFileAsBlob("StaffMember-photo-Bar.pdf");
-        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto/invoke");
-
-        /*
-         *  String name,
-         *  Department.SecondaryKey departmentSecondaryKey,
-         *  Blob photo
-         */
-        var args = restfulClient.arguments()
-                .addActionParameter("name", staffName)
-                //.addActionParameter("departmentSecondaryKey", Map.of("name", 
"Classics")) ... can be used alternatively
-                .addActionParameter("departmentSecondaryKey", 
Department.SecondaryKey.class, new Department.SecondaryKey("Classics"))
-                .addActionParameter("photo", photo)
-                .build();
-
-        // when
-        val response = requestBuilder.post(args);
-
-        // then
-        
assertThat(response.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
-        
assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode());
-
-        // and also json response
-        val entity = response.readEntity(String.class);
-        assertThat(response)
-                .extracting(Response::getStatus)
-                .isEqualTo(Response.Status.OK.getStatusCode());
-        Approvals.verify(entity, jsonOptions());
-
-        // 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 Blob readFileAsBlob(final String fileName) {
-        var bytes = DataSource.ofResource(Abstract_IntegTest.class, fileName)
-                .bytes();
-        return Blob.of(fileName, CommonMimeType.PDF, bytes);
-    }
-
-}
-
diff --git 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.approved.json
new file mode 100644
index 0000000000..4b6928e173
--- /dev/null
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto.approved.json
@@ -0,0 +1,17 @@
+{
+  "name" : {
+    "value" : "Fred Smith"
+  },
+  "departmentSecondaryKey" : {
+    "value" : {
+      "name" : "Classics"
+    }
+  },
+  "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_hilevel_IntegTest.createStaffMemberWithPhoto2.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto2.approved.json
new file mode 100644
index 0000000000..a6123f9fb9
--- /dev/null
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto2.approved.json
@@ -0,0 +1,17 @@
+{
+  "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_hilevel_IntegTest.createStaffMemberWithPhoto2_using_map.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto2_using_map.approved.json
new file mode 100644
index 0000000000..a6123f9fb9
--- /dev/null
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto2_using_map.approved.json
@@ -0,0 +1,17 @@
+{
+  "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_hilevel_IntegTest.createStaffMemberWithPhoto_using_map.approved.json
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto_using_map.approved.json
new file mode 100644
index 0000000000..4b6928e173
--- /dev/null
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.createStaffMemberWithPhoto_using_map.approved.json
@@ -0,0 +1,17 @@
+{
+  "name" : {
+    "value" : "Fred Smith"
+  },
+  "departmentSecondaryKey" : {
+    "value" : {
+      "name" : "Classics"
+    }
+  },
+  "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_hilevel_IntegTest.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.java
new file mode 100644
index 0000000000..b1e02c753f
--- /dev/null
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_hilevel_IntegTest.java
@@ -0,0 +1,278 @@
+/*
+ *  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 javax.ws.rs.core.Response;
+
+import org.apache.causeway.applib.services.bookmark.Bookmark;
+
+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 org.springframework.transaction.annotation.Propagation;
+
+import org.apache.causeway.applib.value.Blob;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+import org.apache.causeway.commons.io.DataSource;
+import org.apache.causeway.viewer.restfulobjects.test.domain.dom.Department;
+import 
org.apache.causeway.viewer.restfulobjects.test.scenarios.Abstract_IntegTest;
+
+import lombok.SneakyThrows;
+import lombok.val;
+
+import java.util.Map;
+
+class Staff_hilevel_IntegTest extends Abstract_IntegTest {
+
+    @SneakyThrows
+    @Test
+    @UseReporter(DiffReporter.class)
+    public void createStaffMemberWithPhoto() {
+
+        // 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();
+
+        final Blob photo = readFileAsBlob("StaffMember-photo-Bar.pdf");
+        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto/invoke");
+
+        /*
+         *  String name,
+         *  Department.SecondaryKey departmentSecondaryKey,
+         *  Blob photo
+         */
+        var args = restfulClient.arguments()
+                .addActionParameter("name", staffName)
+                .addActionParameter("departmentSecondaryKey", 
Department.SecondaryKey.class, new Department.SecondaryKey("Classics"))
+                .addActionParameter("photo", photo)
+                .build();
+
+        Approvals.verify(args.getEntity(), jsonOptions());
+
+        // when
+        val response = requestBuilder.post(args);
+
+        // then
+        
assertThat(response.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
+        
assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode());
+
+        // and also json response
+        val entity = response.readEntity(String.class);
+        assertThat(response)
+                .extracting(Response::getStatus)
+                .isEqualTo(Response.Status.OK.getStatusCode());
+
+        // 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();
+    }
+
+    @SneakyThrows
+    @Test
+    @UseReporter(DiffReporter.class)
+    public void createStaffMemberWithPhoto_using_map() {
+
+        // 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();
+
+        final Blob photo = readFileAsBlob("StaffMember-photo-Bar.pdf");
+        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto/invoke");
+
+        var args = restfulClient.arguments()
+                .addActionParameter("name", staffName)
+                .addActionParameter("departmentSecondaryKey", Map.of("name", 
"Classics"))
+                .addActionParameter("photo", photo)
+                .build();
+
+        Approvals.verify(args.getEntity(), jsonOptions());
+
+        // when
+        val response = requestBuilder.post(args);
+
+        // then
+        
assertThat(response.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
+        
assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode());
+
+        // and also json response
+        val entity = response.readEntity(String.class);
+        assertThat(response)
+                .extracting(Response::getStatus)
+                .isEqualTo(Response.Status.OK.getStatusCode());
+
+        // 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();
+    }
+
+    @SneakyThrows
+    @Test
+    @UseReporter(DiffReporter.class)
+    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();
+
+        // and given
+        final Blob photo = readFileAsBlob("StaffMember-photo-Bar.pdf");
+        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto2/invoke");
+
+        var args = restfulClient.arguments()
+                .addActionParameter("name", staffName)
+                .addActionParameter("department", departmentBookmark)
+                .addActionParameter("photo", photo)
+                .build();
+
+        Approvals.verify(args.getEntity(), jsonOptions());
+
+        // when
+        val response = requestBuilder.post(args);
+
+        // then
+        
assertThat(response.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
+        
assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode());
+
+        // and also json response
+        val entity = response.readEntity(String.class);
+        assertThat(response)
+                .extracting(Response::getStatus)
+                .isEqualTo(Response.Status.OK.getStatusCode());
+
+        // 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();
+    }
+
+    @SneakyThrows
+    @Test
+    @UseReporter(DiffReporter.class)
+    public void createStaffMemberWithPhoto2_using_map() {
+
+        // 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();
+
+        // and given
+        final Blob photo = readFileAsBlob("StaffMember-photo-Bar.pdf");
+        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto2/invoke");
+
+        /*
+         *  String name,
+         *  Department.SecondaryKey departmentSecondaryKey,
+         *  Blob photo
+         */
+        var args = restfulClient.arguments()
+                .addActionParameter("name", staffName)
+                .addActionParameter("department", Map.of("href", 
asAbsoluteHref(departmentBookmark)))
+                .addActionParameter("photo", photo)
+                .build();
+
+        Approvals.verify(args.getEntity(), jsonOptions());
+
+        // when
+        val response = requestBuilder.post(args);
+
+        // then
+        
assertThat(response.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
+        
assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode());
+
+        // and also json response
+        val entity = response.readEntity(String.class);
+        assertThat(response)
+                .extracting(Response::getStatus)
+                .isEqualTo(Response.Status.OK.getStatusCode());
+
+        // 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 Blob readFileAsBlob(final String fileName) {
+        var bytes = DataSource.ofResource(Abstract_IntegTest.class, fileName)
+                .bytes();
+        return Blob.of(fileName, CommonMimeType.PDF, bytes);
+    }
+
+    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.getLogicalTypeName(), 
bookmark.getIdentifier());
+    }
+
+
+}
+
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
new file mode 100644
index 0000000000..5c1d966721
--- /dev/null
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.createStaffMemberWithPhoto2.approved.json
@@ -0,0 +1,13 @@
+{
+  "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_Orig_IntegTest.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.java
similarity index 82%
copy from 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_Orig_IntegTest.java
copy to 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.java
index cafae75234..150f332917 100644
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_Orig_IntegTest.java
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v1_IntegTest.java
@@ -29,6 +29,7 @@ import javax.ws.rs.core.Response;
 import com.google.common.io.Resources;
 import com.google.gson.GsonBuilder;
 
+import org.approvaltests.Approvals;
 import org.approvaltests.reporters.DiffReporter;
 import org.approvaltests.reporters.UseReporter;
 import org.junit.jupiter.api.BeforeEach;
@@ -45,7 +46,7 @@ import lombok.Getter;
 import lombok.SneakyThrows;
 import lombok.val;
 
-public class Staff_Orig_IntegTest extends Abstract_IntegTest {
+public class Staff_lowlevel_v1_IntegTest extends Abstract_IntegTest {
 
     private GsonBuilder gsonBuilder;
 
@@ -57,7 +58,7 @@ public class Staff_Orig_IntegTest extends Abstract_IntegTest {
     @SneakyThrows
     @Test
     @UseReporter(DiffReporter.class)
-    public void can_create_staff_member() {
+    public void createStaffMemberWithPhoto2() {
 
         // given
         final var staffName = "Fred Smith";
@@ -67,27 +68,33 @@ public class Staff_Orig_IntegTest extends 
Abstract_IntegTest {
             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();
 
-        assertThat(bookmarkBeforeIfAny).isEmpty();
-
-        final var photoEncoded = 
readFileAndEncodeAsBlob("StaffMember-photo-Bar.pdf");
         String departmentHref = asRelativeHref(departmentBookmark);
         Invocation.Builder departmentRequest = 
restfulClient.request(departmentHref);
         Response departmentResponse = departmentRequest.get();
         
assertThat(departmentResponse.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
 
-        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto/invoke");
+        // 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 = gsonBuilder.create().toJson(body);
 
-        // when
+        // then
+        Approvals.verify(bodyJson, jsonOptions());
+
+        // and when send request
         val response = requestBuilder.post(Entity.entity(bodyJson, 
"application/json"));
 
         // then
@@ -96,7 +103,6 @@ public class Staff_Orig_IntegTest extends Abstract_IntegTest 
{
         
assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode());
 
         // and also json response
-        // Approvals.verify(entity, jsonOptions());
 
         // and also object is created in database
         final var bookmarkAfterIfAny = 
transactionService.callTransactional(Propagation.REQUIRED, () -> {
@@ -106,14 +112,14 @@ public class Staff_Orig_IntegTest extends 
Abstract_IntegTest {
         assertThat(bookmarkAfterIfAny).isNotEmpty();
     }
 
-    private String asRelativeHref(Bookmark bookmark) {
-        return String.format("objects/%s/%s", bookmark.getLogicalTypeName(), 
bookmark.getIdentifier());
-    }
-
     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.getLogicalTypeName(), 
bookmark.getIdentifier());
+    }
+
     private String readFileAndEncodeAsBlob(String fileName) throws 
IOException, URISyntaxException {
         byte[] bytes = 
Resources.toByteArray(Resources.getResource(Abstract_IntegTest.class, 
fileName));
         String photoEncoded = encodePdf(fileName, bytes);
@@ -135,16 +141,14 @@ public class Staff_Orig_IntegTest extends 
Abstract_IntegTest {
          * @param blobValue - is the Blob encoded format: 
"filename.pdf:application/pdf:pdfBytesBase64Encoded"
          */
         Body(String nameValue, String departmentHrefValue, String blobValue) {
-            photo = new 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Blob(blobValue);
-            name = new 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Name(nameValue);
-            department = new 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Department(new
 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Value(departmentHrefValue));
+            photo = new Blob(blobValue);
+            name = new Name(nameValue);
+            department = new Department(new 
Department.Value(departmentHrefValue));
         }
 
-        private 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Name name;
-
-        private 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Department 
department;
-
-        private 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Blob photo;
+        private Name name;
+        private Department department;
+        private Blob photo;
 
         @lombok.Value
         static class Name {
@@ -153,22 +157,21 @@ public class Staff_Orig_IntegTest extends 
Abstract_IntegTest {
 
         @lombok.Value
         static class Department {
-            private 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Value value;
-        }
+            private Value value;
+
+            @lombok.Value
+            static class Value {
+                private String href;
+            }
 
-        @lombok.Value
-        static class Value {
-            private String href;
         }
 
         @lombok.Value
         static class Blob {
             private String value;
         }
-
     }
 
-
 }
 
 
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
new file mode 100644
index 0000000000..a6123f9fb9
--- /dev/null
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.createStaffMemberWithPhoto2.approved.json
@@ -0,0 +1,17 @@
+{
+  "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_Orig_IntegTest.java
 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.java
similarity index 70%
rename from 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_Orig_IntegTest.java
rename to 
viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.java
index cafae75234..26b6eeb8a1 100644
--- 
a/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_Orig_IntegTest.java
+++ 
b/viewers/restfulobjects/test/src/test/java/org/apache/causeway/viewer/restfulobjects/test/scenarios/staff/Staff_lowlevel_v2_IntegTest.java
@@ -19,7 +19,6 @@
 package org.apache.causeway.viewer.restfulobjects.test.scenarios.staff;
 
 import java.io.IOException;
-import java.net.URISyntaxException;
 import java.util.Base64;
 
 import javax.ws.rs.client.Entity;
@@ -29,6 +28,9 @@ import javax.ws.rs.core.Response;
 import com.google.common.io.Resources;
 import com.google.gson.GsonBuilder;
 
+import org.apache.causeway.applib.value.Blob;
+
+import org.approvaltests.Approvals;
 import org.approvaltests.reporters.DiffReporter;
 import org.approvaltests.reporters.UseReporter;
 import org.junit.jupiter.api.BeforeEach;
@@ -45,7 +47,7 @@ import lombok.Getter;
 import lombok.SneakyThrows;
 import lombok.val;
 
-public class Staff_Orig_IntegTest extends Abstract_IntegTest {
+public class Staff_lowlevel_v2_IntegTest extends Abstract_IntegTest {
 
     private GsonBuilder gsonBuilder;
 
@@ -57,7 +59,7 @@ public class Staff_Orig_IntegTest extends Abstract_IntegTest {
     @SneakyThrows
     @Test
     @UseReporter(DiffReporter.class)
-    public void can_create_staff_member() {
+    public void createStaffMemberWithPhoto2() {
 
         // given
         final var staffName = "Fred Smith";
@@ -67,27 +69,33 @@ public class Staff_Orig_IntegTest extends 
Abstract_IntegTest {
             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();
 
-        assertThat(bookmarkBeforeIfAny).isEmpty();
-
-        final var photoEncoded = 
readFileAndEncodeAsBlob("StaffMember-photo-Bar.pdf");
         String departmentHref = asRelativeHref(departmentBookmark);
         Invocation.Builder departmentRequest = 
restfulClient.request(departmentHref);
         Response departmentResponse = departmentRequest.get();
         
assertThat(departmentResponse.getStatusInfo().getFamily()).isEqualTo(Response.Status.Family.SUCCESSFUL);
 
-        final var requestBuilder = 
restfulClient.request("services/university.dept.Staff/actions/createStaffMemberWithPhoto/invoke");
+        // 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 = gsonBuilder.create().toJson(body);
 
-        // when
+        // then
+        Approvals.verify(bodyJson, jsonOptions());
+
+        // and when send request
         val response = requestBuilder.post(Entity.entity(bodyJson, 
"application/json"));
 
         // then
@@ -96,7 +104,6 @@ public class Staff_Orig_IntegTest extends Abstract_IntegTest 
{
         
assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode());
 
         // and also json response
-        // Approvals.verify(entity, jsonOptions());
 
         // and also object is created in database
         final var bookmarkAfterIfAny = 
transactionService.callTransactional(Propagation.REQUIRED, () -> {
@@ -106,45 +113,37 @@ public class Staff_Orig_IntegTest extends 
Abstract_IntegTest {
         assertThat(bookmarkAfterIfAny).isNotEmpty();
     }
 
-    private String asRelativeHref(Bookmark bookmark) {
-        return String.format("objects/%s/%s", bookmark.getLogicalTypeName(), 
bookmark.getIdentifier());
-    }
-
     private String asAbsoluteHref(Bookmark bookmark) {
         return String.format("%s%s", 
restfulClient.getConfig().getRestfulBaseUrl(), asRelativeHref(bookmark));
     }
 
-    private String readFileAndEncodeAsBlob(String fileName) throws 
IOException, URISyntaxException {
-        byte[] bytes = 
Resources.toByteArray(Resources.getResource(Abstract_IntegTest.class, 
fileName));
-        String photoEncoded = encodePdf(fileName, bytes);
-        return photoEncoded;
+    private String asRelativeHref(Bookmark bookmark) {
+        return String.format("objects/%s/%s", bookmark.getLogicalTypeName(), 
bookmark.getIdentifier());
     }
 
-    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;
+    private Blob readFileAsBlob(String fileName) throws IOException {
+        byte[] bytes = 
Resources.toByteArray(Resources.getResource(Abstract_IntegTest.class, 
fileName));
+        return new Blob(fileName, "application/pdf", bytes);
     }
 
+
     @Getter
     static class Body {
 
         /**
          * @param nameValue
          * @param departmentHrefValue
-         * @param blobValue - is the Blob encoded format: 
"filename.pdf:application/pdf:pdfBytesBase64Encoded"
+         * @param blob - is the Blob to be formatted
          */
-        Body(String nameValue, String departmentHrefValue, String blobValue) {
-            photo = new 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Blob(blobValue);
-            name = new 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Name(nameValue);
-            department = new 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Department(new
 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Value(departmentHrefValue));
+        Body(String nameValue, String departmentHrefValue, 
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.getName(), 
blob.getMimeType().toString(), 
Base64.getEncoder().encodeToString(blob.getBytes())));
         }
 
-        private 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Name name;
-
-        private 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Department 
department;
-
-        private 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Blob photo;
+        private Name name;
+        private Department department;
+        private Blob photo;
 
         @lombok.Value
         static class Name {
@@ -153,22 +152,30 @@ public class Staff_Orig_IntegTest extends 
Abstract_IntegTest {
 
         @lombok.Value
         static class Department {
-            private 
org.apache.causeway.viewer.restfulobjects.test.scenarios.staff.Body.Value value;
-        }
+            private Value value;
+
+            @lombok.Value
+            static class Value {
+                private String href;
+            }
 
-        @lombok.Value
-        static class Value {
-            private String href;
         }
 
+
         @lombok.Value
         static class Blob {
-            private String value;
+            private Value value;
+
+            @lombok.Value
+            static class Value {
+                private String name;
+                private String mimeType;
+                private String bytes;
+            }
         }
 
     }
 
-
 }
 
 

Reply via email to