This is an automated email from the ASF dual-hosted git repository. danhaywood pushed a commit to branch CAUSEWAY-3676 in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 5aa9317523310b6f7469aeb4f8c96c7e4d913362 Author: danhaywood <[email protected]> AuthorDate: Thu Feb 15 00:00:49 2024 +0000 CAUSEWAY-3676: adds test to download from URL --- ...est.find_department_and_add_staff_members._.gql | 70 ++++++++++++++++ ...d_department_and_add_staff_members._.invoke.gql | 27 ------ ..._department_and_add_staff_members.approved.json | 97 +++++++++++++++++++--- ..._department_and_add_staff_members.choices._.gql | 22 ----- .../queryandmutations/Department_IntegTest.java | 22 +---- ...d_staff_member_by_name_and_download_photo._.gql | 16 ++++ ...member_by_name_and_download_photo.approved.json | 18 ++++ .../e2e/queryandmutations/Staff_IntegTest.java | 47 +++++++++++ .../viewer/CausewayModuleViewerGraphqlViewer.java | 7 +- .../viewer/controller/BlobBytesController.java | 50 ++++++----- 10 files changed, 271 insertions(+), 105 deletions(-) diff --git a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members._.gql b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members._.gql new file mode 100644 index 0000000000..8363554ae5 --- /dev/null +++ b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members._.gql @@ -0,0 +1,70 @@ +{ + Scenario(name: "can add staff members") { + Given { + university_dept_Departments { + findDepartmentByName { + invoke(name: "Classics") { + staffMembers { + get { + name { + get + } + } + } + } + } + } + } + When { + university_dept_Departments { + findDepartmentByName { + invoke(name: "Classics") { + addStaffMembers { + params { + staffMembers { + datatype + choices { + _gqlv_meta { + id + saveAs(ref: "staff-member") + } + name { + get + } + } + } + } + invokeIdempotent(staffMembers: [{ref: "staff-member"}, {ref: "staff-member-2"}]) { + name { + get + } + staffMembers { + get { + name { + get + } + } + } + } + } + } + } + } + } + Then { + university_dept_Departments { + findDepartmentByName { + invoke(name: "Classics") { + staffMembers { + get { + name { + get + } + } + } + } + } + } + } + } +} diff --git a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members._.invoke.gql b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members._.invoke.gql deleted file mode 100644 index 1b5452b974..0000000000 --- a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members._.invoke.gql +++ /dev/null @@ -1,27 +0,0 @@ -{ - university_dept_Departments { - findDepartmentByName { - invoke(name: "Classics") { - addStaffMembers { - params { - staffMembers { - datatype - } - } - invokeIdempotent(staffMembers: [{id: "$staffMemberId1"}, {id: "$staffMemberId2"}]) { - name { - get - } - staffMembers { - get { - name { - get - } - } - } - } - } - } - } - } -} diff --git a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members.approved.json b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members.approved.json index 8126e9ff36..960446e13a 100644 --- a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members.approved.json +++ b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members.approved.json @@ -1,18 +1,93 @@ { "data" : { - "university_dept_Departments" : { - "findDepartmentByName" : { - "invoke" : { - "addStaffMembers" : { - "params" : { + "Scenario" : { + "Given" : { + "university_dept_Departments" : { + "findDepartmentByName" : { + "invoke" : { "staffMembers" : { - "datatype" : "university_dept_StaffMember" + "get" : [ { + "name" : { + "get" : "Gerry Jones" + } + }, { + "name" : { + "get" : "Letitia Leadbetter" + } + } ] } - }, - "invokeIdempotent" : { - "name" : { - "get" : "Classics" - }, + } + } + } + }, + "When" : { + "university_dept_Departments" : { + "findDepartmentByName" : { + "invoke" : { + "addStaffMembers" : { + "params" : { + "staffMembers" : { + "datatype" : "university_dept_StaffMember", + "choices" : [ { + "_gqlv_meta" : { + "id" : "15", + "saveAs" : "staff-member" + }, + "name" : { + "get" : "John Gartner" + } + }, { + "_gqlv_meta" : { + "id" : "16", + "saveAs" : "staff-member" + }, + "name" : { + "get" : "Margaret Randall" + } + }, { + "_gqlv_meta" : { + "id" : "14", + "saveAs" : "staff-member" + }, + "name" : { + "get" : "Mervin Hughes" + } + } ] + } + }, + "invokeIdempotent" : { + "name" : { + "get" : "Classics" + }, + "staffMembers" : { + "get" : [ { + "name" : { + "get" : "Gerry Jones" + } + }, { + "name" : { + "get" : "John Gartner" + } + }, { + "name" : { + "get" : "Letitia Leadbetter" + } + }, { + "name" : { + "get" : "Margaret Randall" + } + } ] + } + } + } + } + } + } + }, + "Then" : { + "university_dept_Departments" : { + "findDepartmentByName" : { + "invoke" : { "staffMembers" : { "get" : [ { "name" : { diff --git a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members.choices._.gql b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members.choices._.gql deleted file mode 100644 index 0c429c93b9..0000000000 --- a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.find_department_and_add_staff_members.choices._.gql +++ /dev/null @@ -1,22 +0,0 @@ -{ - university_dept_Departments { - findDepartmentByName { - invoke(name: "Classics") { - addStaffMembers { - params { - staffMembers { - choices { - _gqlv_meta { - id - } - name { - get - } - } - } - } - } - } - } - } -} diff --git a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.java b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.java index 5db64cac19..a24e276e2f 100644 --- a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.java +++ b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Department_IntegTest.java @@ -127,28 +127,8 @@ public class Department_IntegTest extends Abstract_IntegTest { void find_department_and_add_staff_members() throws Exception { // when, then - String submit = submit("choices"); - - ObjectMapper mapper = new ObjectMapper(); - JsonNode root = mapper.readTree(submit); - - JsonNode staffMembersNode = root.at("/data/university_dept_Departments/findDepartmentByName/invoke/addStaffMembers/params/staffMembers/choices"); - - List<String> ids = new ArrayList<>(); - staffMembersNode.forEach(staffMemberNode -> { - String id = staffMemberNode.get("_gqlv_meta").get("id").asText(); - if (!_Strings.isNullOrEmpty(id)) { - ids.add(id); - } - }); - - Assertions.assertThat(ids).hasSize(3); - - val replacements = _Maps.unmodifiable( - "$staffMemberId1", ids.get(0), - "$staffMemberId2", ids.get(1)); + Approvals.verify(submit(), jsonOptions()); - Approvals.verify(submit("invoke", replacements), jsonOptions()); } @Test diff --git a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.find_staff_member_by_name_and_download_photo._.gql b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.find_staff_member_by_name_and_download_photo._.gql new file mode 100644 index 0000000000..51ac1345c4 --- /dev/null +++ b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.find_staff_member_by_name_and_download_photo._.gql @@ -0,0 +1,16 @@ +{ + university_dept_Staff { + findStaffMemberByName { + invoke(name: "Gerry Jones") { + name { + get + } + photo { + get { + bytes + } + } + } + } + } +} diff --git a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.find_staff_member_by_name_and_download_photo.approved.json b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.find_staff_member_by_name_and_download_photo.approved.json new file mode 100644 index 0000000000..8fe6c5418c --- /dev/null +++ b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.find_staff_member_by_name_and_download_photo.approved.json @@ -0,0 +1,18 @@ +{ + "data" : { + "university_dept_Staff" : { + "findStaffMemberByName" : { + "invoke" : { + "name" : { + "get" : "Gerry Jones" + }, + "photo" : { + "get" : { + "bytes" : "///graphql/object/university.dept.StaffMember:13/photo/blobBytes" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.java b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.java index c06c622d7a..0dbe1d0d77 100644 --- a/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.java +++ b/viewers/graphql/test/src/test/java/org/apache/causeway/viewer/graphql/viewer/test/e2e/queryandmutations/Staff_IntegTest.java @@ -18,6 +18,11 @@ */ package org.apache.causeway.viewer.graphql.viewer.test.e2e.queryandmutations; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.util.Optional; import org.approvaltests.Approvals; @@ -36,6 +41,9 @@ import org.apache.causeway.applib.services.bookmark.Bookmark; import org.apache.causeway.viewer.graphql.viewer.test.domain.dept.StaffMember; import org.apache.causeway.viewer.graphql.viewer.test.e2e.Abstract_IntegTest; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + //NOT USING @Transactional since we are running server within same transaction otherwise @Order(60) @@ -112,4 +120,43 @@ public class Staff_IntegTest extends Abstract_IntegTest { Approvals.verify(submit(), jsonOptions()); } + + @Test + @UseReporter(DiffReporter.class) + void find_staff_member_by_name_and_download_photo() throws Exception { + + String response = submit(); + Approvals.verify(response, jsonOptions()); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(response); + + String url = root + .at("/data/university_dept_Staff/findStaffMemberByName/invoke/photo/get/bytes") + .asText(); + + assertThat(url).matches("///graphql/object/university.dept.StaffMember:(\\d+)/photo/blobBytes"); + + val httpResponse = submitReturningBytes(url); + + assertThat(httpResponse.statusCode()).isEqualTo(200); + byte[] bytes = httpResponse.body(); + assertThat(bytes).isNotEmpty(); + + } + + private HttpResponse<byte[]> submitReturningBytes(String url) throws IOException, InterruptedException { + + val urlSuffix = url.substring(3); // strip off the '///' prefix + val uri = URI.create(String.format("http://0.0.0.0:%d/%s", port, urlSuffix)); + + HttpRequest httpRequest = HttpRequest.newBuilder(). + uri(uri). + GET(). + build(); + + HttpClient httpClient = HttpClient.newHttpClient(); + return httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray()); + } + } diff --git a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/CausewayModuleViewerGraphqlViewer.java b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/CausewayModuleViewerGraphqlViewer.java index ae85d6e6da..1c28790e91 100644 --- a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/CausewayModuleViewerGraphqlViewer.java +++ b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/CausewayModuleViewerGraphqlViewer.java @@ -18,6 +18,8 @@ */ package org.apache.causeway.viewer.graphql.viewer; +import org.apache.causeway.viewer.graphql.viewer.controller.BlobBytesController; + import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration; import org.springframework.boot.autoconfigure.graphql.GraphQlCorsProperties; @@ -42,7 +44,10 @@ import org.apache.causeway.viewer.graphql.model.CausewayModuleViewerGraphqlModel // autoconfigurations GraphQlAutoConfiguration.class, - GraphQlWebMvcAutoConfiguration.class + GraphQlWebMvcAutoConfiguration.class, + + // controllers + BlobBytesController.class }) @EnableConfigurationProperties({ GraphQlProperties.class, GraphQlCorsProperties.class diff --git a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/BlobBytesController.java b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/BlobBytesController.java index fc5ae3e523..a72670ce79 100644 --- a/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/BlobBytesController.java +++ b/viewers/graphql/viewer/src/main/java/org/apache/causeway/viewer/graphql/viewer/controller/BlobBytesController.java @@ -10,6 +10,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.apache.causeway.applib.services.bookmark.Bookmark; @@ -23,13 +24,37 @@ import lombok.Value; import java.util.Optional; -@RestController(value = "/graphql/object") +@RestController() +@RequestMapping("/graphql/object") @RequiredArgsConstructor(onConstructor_ = {@Inject}) public class BlobBytesController { private final BookmarkService bookmarkService; private final ObjectManager objectManager; + @GetMapping(value = "/{logicalTypeName}:{id}/{propertyId}/blobBytes") + public ResponseEntity<byte[]> propertyBlobBytes( + @PathVariable final String logicalTypeName, + @PathVariable final String id, + @PathVariable final String propertyId) { + + return bookmarkService.lookup(Bookmark.forLogicalTypeNameAndIdentifier(logicalTypeName, id)) + .map(objectManager::adapt) + .map(managedObject -> ManagedObjectAndPropertyIfAny.of(managedObject, managedObject.getSpecification().getProperty(propertyId))) + .filter(ManagedObjectAndPropertyIfAny::isPropertyPresent) + .map(ManagedObjectAndProperty::of) + .map(ManagedObjectAndProperty::value) + .map(ManagedObject::getPojo) + .filter(Blob.class::isInstance) + .map(Blob.class::cast) + .map(blob -> ResponseEntity.ok() + .contentType(MediaType.APPLICATION_PDF) + .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(blob.getName()).build().toString()) + .contentLength(blob.getBytes().length) + .body(blob.getBytes())) + .orElse(ResponseEntity.notFound().build()); + } + @Value(staticConstructor = "of") private static class ManagedObjectAndPropertyIfAny { ManagedObject owningObject; @@ -50,31 +75,10 @@ public class BlobBytesController { ManagedObject owningObject; OneToOneAssociation property; - public ManagedObject value() { + ManagedObject value() { return property.get(owningObject); } } - @GetMapping(value = "/{logicalTypeName}:{id}/{propertyId}/blobBytes") - public ResponseEntity<byte[]> propertyBlobBytes( - @PathVariable final String logicalTypeName, - @PathVariable final String id, - @PathVariable final String propertyId) { - return bookmarkService.lookup(Bookmark.forLogicalTypeNameAndIdentifier(logicalTypeName, id)) - .map(objectManager::adapt) - .map(managedObject -> ManagedObjectAndPropertyIfAny.of(managedObject, managedObject.getSpecification().getProperty(propertyId))) - .filter(ManagedObjectAndPropertyIfAny::isPropertyPresent) - .map(ManagedObjectAndProperty::of) - .map(ManagedObjectAndProperty::value) - .map(ManagedObject::getPojo) - .filter(Blob.class::isInstance) - .map(Blob.class::cast) - .map(blob -> ResponseEntity.ok() - .contentType(MediaType.APPLICATION_PDF) - .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename(blob.getName()).build().toString()) - .contentLength(blob.getBytes().length) - .body(blob.getBytes())) - .orElse(ResponseEntity.notFound().build()); - } }
