diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/CallFailedRuntimeException.java b/fineract-client/src/main/java/org/apache/fineract/client/util/CallFailedRuntimeException.java index 671739469..333daa715 100644 --- a/fineract-client/src/main/java/org/apache/fineract/client/util/CallFailedRuntimeException.java +++ b/fineract-client/src/main/java/org/apache/fineract/client/util/CallFailedRuntimeException.java @@ -23,7 +23,7 @@ import retrofit2.Call; import retrofit2.Response; /** - * Exception thrown by {@link Calls#ok(Call)} when {@link Call}s fail. + * Exception thrown by {@link Calls} utility when {@link Call}s fail. * * @author Michael Vorburger.ch */ diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/Calls.java b/fineract-client/src/main/java/org/apache/fineract/client/util/Calls.java index 50f82ef7f..79d9e08c9 100644 --- a/fineract-client/src/main/java/org/apache/fineract/client/util/Calls.java +++ b/fineract-client/src/main/java/org/apache/fineract/client/util/Calls.java @@ -32,7 +32,8 @@ public final class Calls { private Calls() {} /** - * Execute a Call, expecting success. + * Execute a Call, expecting success, returning strongly typed body. This covers the most typical use and is thus + * most used. * * @param call * the Call to execute @@ -42,13 +43,38 @@ public final class Calls { * [200..300) successful */ public static T ok(Call call) throws CallFailedRuntimeException { + return okR(call).body(); + } + + /** + * Execute a Call, expecting success, returning Response. This is rarely used, and only useful if you need access to + * e.g. the headers of the response. + * + * @param call + * the Call to execute + * @return the Response of the successful call (never null) + * @throws CallFailedRuntimeException + * thrown either if a problem occurred talking to the server, or the HTTP response code was not + * [200..300) successful + */ + public static Response okR(Call call) throws CallFailedRuntimeException { Response response = executeU(call); if (response.isSuccessful()) { - return response.body(); + return response; } throw new CallFailedRuntimeException(call, response); } + /** + * {@link Call#execute()} mapping {@link IOException} to {@link CallFailedRuntimeException}. This can be useful for + * code which would like to handle non-success HTTP responses, notably tests asserting non-200 results. + * + * @param call + * the Call to execute + * @return the Response, which may or may not have been successful. + * @throws CallFailedRuntimeException + * thrown if a problem occurred talking to the server + */ public static Response executeU(Call call) throws CallFailedRuntimeException { try { return call.execute(); diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/Parts.java b/fineract-client/src/main/java/org/apache/fineract/client/util/Parts.java index 40e4ffe95..8cea0a15c 100644 --- a/fineract-client/src/main/java/org/apache/fineract/client/util/Parts.java +++ b/fineract-client/src/main/java/org/apache/fineract/client/util/Parts.java @@ -19,13 +19,22 @@ package org.apache.fineract.client.util; import java.io.File; +import java.util.Optional; import okhttp3.MediaType; import okhttp3.MultipartBody.Part; import okhttp3.RequestBody; +import org.apache.fineract.client.services.DocumentsApiFixed; +import org.apache.fineract.client.services.ImagesApi; +import retrofit2.Response; /** * Convenience Factory for {@link Part} (including {@link RequestBody}). * + * {@link Part} is the argument of operations for binary uploads like + * {@link DocumentsApiFixed#createDocument(String, Long, Part, String, String)}, + * {@link DocumentsApiFixed#updateDocument(String, Long, Long, Part, String, String)} and + * {@link ImagesApi#create(String, Long, Part)} and {@link ImagesApi#update(String, Long, Part)}. + * * @author Michael Vorburger.ch */ public final class Parts { @@ -85,4 +94,16 @@ public final class Parts { return null; } } + + public static Optional fileName(Response response) { + String contentDisposition = response.headers().get("Content-Disposition"); + if (contentDisposition == null) { + return Optional.empty(); + } + int i = contentDisposition.indexOf("; filename=\""); + if (i == -1) { + return Optional.empty(); + } + return Optional.of(contentDisposition.substring(i + "; filename=\"".length(), contentDisposition.length() - 1)); + } } diff --git a/fineract-client/src/test/java/org/apache/fineract/client/util/PartsTest.java b/fineract-client/src/test/java/org/apache/fineract/client/util/PartsTest.java index a312ff8e4..25ce8eb17 100644 --- a/fineract-client/src/test/java/org/apache/fineract/client/util/PartsTest.java +++ b/fineract-client/src/test/java/org/apache/fineract/client/util/PartsTest.java @@ -19,8 +19,11 @@ package org.apache.fineract.client.util; import com.google.common.truth.Truth; +import com.google.common.truth.Truth8; +import okhttp3.Headers; import okhttp3.MediaType; import org.junit.jupiter.api.Test; +import retrofit2.Response; public class PartsTest { @@ -43,4 +46,15 @@ public class PartsTest { void nullMediaType() { Truth.assertThat(Parts.mediaType(null)).isNull(); } + + @Test + void fileName() { + Truth8.assertThat(Parts.fileName(Response.success(null, Headers.of("Content-Disposition", "attachment; filename=\"doc.pdf\"")))) + .hasValue("doc.pdf"); + } + + @Test + void fileNameWithoutContentDisposition() { + Truth8.assertThat(Parts.fileName(Response.success(null))).isEmpty(); + } } diff --git a/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/DocumentTest.java b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/DocumentTest.java index 8ce7bf308..fed52b895 100644 --- a/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/DocumentTest.java +++ b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/DocumentTest.java @@ -28,6 +28,7 @@ import org.apache.fineract.client.models.PostEntityTypeEntityIdDocumentsResponse import org.apache.fineract.client.util.Parts; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import retrofit2.Response; /** * Integration Test for /documents API. @@ -80,15 +81,18 @@ public class DocumentTest extends IntegrationTest { @Test @Order(4) void downloadFile() throws IOException { - ResponseBody r = ok(fineract().documents.downloadFile("clients", clientId, documentId)); - assertThat(r.contentType()).isEqualTo(MediaType.get("image/jpeg")); - assertThat(r.bytes().length).isEqualTo(testFile.length()); - assertThat(r.contentLength()).isEqualTo(-1); // TODO testFile.length() + Response r = okR(fineract().documents.downloadFile("clients", clientId, documentId)); + try (ResponseBody body = r.body()) { + assertThat(body.contentType()).isEqualTo(MediaType.get("image/jpeg")); + assertThat(body.bytes().length).isEqualTo(testFile.length()); + assertThat(body.contentLength()).isEqualTo(-1); // TODO testFile.length() + } + assertThat(Parts.fileName(r)).hasValue(testFile.getName()); } @Test @Order(10) - void updateDocument() { + void updateDocumentWithoutNewUpload() { String newName = "Test changed name"; String newDescription = getClass().getName(); ok(fineract().documents.updateDocument("clients", clientId, documentId, null, newName, newDescription)); diff --git a/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/ImageTest.java b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/ImageTest.java index 1f8d58ffe..56b951e46 100644 --- a/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/ImageTest.java +++ b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/ImageTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import retrofit2.Call; +import retrofit2.Response; import retrofit2.http.GET; import retrofit2.http.Headers; @@ -83,10 +84,15 @@ public class ImageTest extends IntegrationTest { @Order(5) void getInlineOctetOutput() throws IOException { // 3505x1972 is the exact original size of testFile - ResponseBody r = ok(fineract().images.get("staff", 1L, 3505, 1972, "inline_octet")); - assertThat(r.contentType()).isEqualTo(MediaType.get("image/jpeg")); - assertThat(r.bytes().length).isEqualTo(testImage.length()); - assertThat(r.contentLength()).isEqualTo(testImage.length()); + Response r = okR(fineract().images.get("staff", 1L, 3505, 1972, "inline_octet")); + try (ResponseBody body = r.body()) { + assertThat(body.contentType()).isEqualTo(MediaType.get("image/jpeg")); + assertThat(body.bytes().length).isEqualTo(testImage.length()); + assertThat(body.contentLength()).isEqualTo(testImage.length()); + } + + String expectedFileName = "test, testJPEG"; // without dot! + assertThat(Parts.fileName(r)).hasValue(expectedFileName); } @Test diff --git a/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/IntegrationTest.java b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/IntegrationTest.java index 80e15eb1f..523d40950 100644 --- a/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/IntegrationTest.java +++ b/fineract-client/src/test/java/org/apache/fineract/integrationtests/newstyle/IntegrationTest.java @@ -43,6 +43,7 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.TestMethodOrder; import retrofit2.Call; +import retrofit2.Response; /** * Integration Test for /documents API. @@ -85,6 +86,10 @@ public abstract class IntegrationTest { return Calls.ok(call); } + protected Response okR(Call call) { + return Calls.okR(call); + } + // as above, avoids import static CallSubject.assertThat protected CallSubject assertThat(Call call) { return CallSubject.assertThat(call);