Add new DocumentTest (FINERACT-1218)

This commit is contained in:
Michael Vorburger
2020-10-23 20:50:00 +03:00
committed by Petri Tuomola
parent 6992102035
commit 20daf59a3e
9 changed files with 434 additions and 5 deletions
@@ -0,0 +1,147 @@
/**
* 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.fineract.client.services;
import java.util.List;
import okhttp3.ResponseBody;
import org.apache.fineract.client.models.DeleteEntityTypeEntityIdDocumentsResponse;
import org.apache.fineract.client.models.GetEntityTypeEntityIdDocumentsResponse;
import org.apache.fineract.client.models.PostEntityTypeEntityIdDocumentsResponse;
import org.apache.fineract.client.models.PutEntityTypeEntityIdDocumentsResponse;
import retrofit2.Call;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
// This class was originally generated by OpenAPI Generator (https://openapi-generator.tech),
// but then had to be manually edited by Michael Vorburger.ch to manually fix https://issues.apache.org/jira/browse/FINERACT-1227.
// If we could fix our OpenAPI / Swagger YAML generation from the JAX RS and OpenAPI annotation to have the correct notation for binary document files and images, then this could be removed again.
public interface DocumentsApiFixed {
/**
* Create a Document Note: A document is created using a Multi-part form upload Body Parts name : Name or summary of
* the document description : Description of the document file : The file to be uploaded Mandatory Fields : file and
* description
*
* @param entityType
* entityType (required)
* @param entityId
* entityId (required)
* @param file
* (required)
* @param name
* name (optional)
* @param description
* description (optional)
* @return Call<PostEntityTypeEntityIdDocumentsResponse>
*/
@retrofit2.http.Multipart
@POST("{entityType}/{entityId}/documents")
Call<PostEntityTypeEntityIdDocumentsResponse> createDocument(@retrofit2.http.Path("entityType") String entityType,
@retrofit2.http.Path("entityId") Long entityId, @retrofit2.http.Part okhttp3.MultipartBody.Part file,
@retrofit2.http.Part("name") String name, @retrofit2.http.Part("description") String description);
/**
* Remove a Document
*
* @param entityType
* entityType (required)
* @param entityId
* entityId (required)
* @param documentId
* documentId (required)
* @return Call&lt;DeleteEntityTypeEntityIdDocumentsResponse&gt;
*/
@DELETE("{entityType}/{entityId}/documents/{documentId}")
Call<DeleteEntityTypeEntityIdDocumentsResponse> deleteDocument(@retrofit2.http.Path("entityType") String entityType,
@retrofit2.http.Path("entityId") Long entityId, @retrofit2.http.Path("documentId") Long documentId);
/**
* Retrieve Binary File associated with Document Request used to download the file associated with the document
* Example Requests: clients/1/documents/1/attachment loans/1/documents/1/attachment
*
* @param entityType
* entityType (required)
* @param entityId
* entityId (required)
* @param documentId
* documentId (required)
* @return Call&lt;Void&gt;
*/
@GET("{entityType}/{entityId}/documents/{documentId}/attachment")
Call<ResponseBody> downloadFile(@retrofit2.http.Path("entityType") String entityType, @retrofit2.http.Path("entityId") Long entityId,
@retrofit2.http.Path("documentId") Long documentId);
/**
* Retrieve a Document Example Requests: clients/1/documents/1 loans/1/documents/1
* client_identifiers/1/documents/1?fields&#x3D;name,description
*
* @param entityType
* entityType (required)
* @param entityId
* entityId (required)
* @param documentId
* documentId (required)
* @return Call&lt;GetEntityTypeEntityIdDocumentsResponse&gt;
*/
@GET("{entityType}/{entityId}/documents/{documentId}")
Call<GetEntityTypeEntityIdDocumentsResponse> getDocument(@retrofit2.http.Path("entityType") String entityType,
@retrofit2.http.Path("entityId") Long entityId, @retrofit2.http.Path("documentId") Long documentId);
/**
* List documents Example Requests: clients/1/documents client_identifiers/1/documents
* loans/1/documents?fields&#x3D;name,description
*
* @param entityType
* entityType (required)
* @param entityId
* entityId (required)
* @return Call&lt;List&lt;GetEntityTypeEntityIdDocumentsResponse&gt;&gt;
*/
@GET("{entityType}/{entityId}/documents")
Call<List<GetEntityTypeEntityIdDocumentsResponse>> retrieveAllDocuments(@retrofit2.http.Path("entityType") String entityType,
@retrofit2.http.Path("entityId") Long entityId);
/**
* Update a Document Note: A document is updated using a Multi-part form upload Body Parts name Name or summary of
* the document description Description of the document file The file to be uploaded
*
* @param entityType
* entityType (required)
* @param entityId
* entityId (required)
* @param documentId
* documentId (required)
* @param file
* (optional)
* @param name
* name (optional)
* @param description
* description (optional)
* @return Call&lt;PutEntityTypeEntityIdDocumentsResponse&gt;
*/
@retrofit2.http.Multipart
@PUT("{entityType}/{entityId}/documents/{documentId}")
Call<PutEntityTypeEntityIdDocumentsResponse> updateDocument(@retrofit2.http.Path("entityType") String entityType,
@retrofit2.http.Path("entityId") Long entityId, @retrofit2.http.Path("documentId") Long documentId,
@retrofit2.http.Part okhttp3.MultipartBody.Part file, @retrofit2.http.Part("name") String name,
@retrofit2.http.Part("description") String description);
}
@@ -56,7 +56,7 @@ import org.apache.fineract.client.services.CodesApi;
import org.apache.fineract.client.services.CurrencyApi;
import org.apache.fineract.client.services.DataTablesApi;
import org.apache.fineract.client.services.DefaultApi;
import org.apache.fineract.client.services.DocumentsApi;
import org.apache.fineract.client.services.DocumentsApiFixed;
import org.apache.fineract.client.services.EntityDataTableApi;
import org.apache.fineract.client.services.EntityFieldConfigurationApi;
import org.apache.fineract.client.services.ExternalServicesApi;
@@ -181,7 +181,7 @@ public final class FineractClient {
public final CurrencyApi currencies;
public final DataTablesApi dataTables;
public final @Deprecated DefaultApi legacy; // TODO FINERACT-1222
public final DocumentsApi documents;
public final DocumentsApiFixed documents;
public final EntityDataTableApi entityDatatableChecks;
public final EntityFieldConfigurationApi entityFieldConfigurations;
public final ExternalServicesApi externalServices;
@@ -288,7 +288,7 @@ public final class FineractClient {
currencies = retrofit.create(CurrencyApi.class);
dataTables = retrofit.create(DataTablesApi.class);
legacy = retrofit.create(DefaultApi.class);
documents = retrofit.create(DocumentsApi.class);
documents = retrofit.create(DocumentsApiFixed.class);
entityDatatableChecks = retrofit.create(EntityDataTableApi.class);
entityFieldConfigurations = retrofit.create(EntityFieldConfigurationApi.class);
externalServices = retrofit.create(ExternalServicesApi.class);
@@ -0,0 +1,86 @@
/**
* 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.fineract.client.util;
import java.io.File;
import okhttp3.MediaType;
import okhttp3.MultipartBody.Part;
import okhttp3.RequestBody;
/**
* Convenience Factory for {@link Part} (including {@link RequestBody}).
*
* @author Michael Vorburger.ch
*/
public final class Parts {
private Parts() {}
public static Part fromFile(File file) {
RequestBody rb = RequestBody.create(file, getMediaType(file.getName()));
return Part.createFormData("file", file.getName(), rb);
}
public static Part fromFile(String fileName, byte[] bytes) {
RequestBody rb = RequestBody.create(bytes, getMediaType(fileName));
return Part.createFormData("file", fileName, rb);
}
// TODO this logic should be on the Server, not have to be done by the client...
// There actually does seem to be some code related to MIME type guessing in Fineract, but test shows it doesn't
// work :(
private static MediaType getMediaType(String fileName) {
int dotPos = fileName.lastIndexOf('.');
if (dotPos == -1) {
return null;
}
String ext = fileName.substring(dotPos);
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
switch (ext) {
case "jpg":
case "jpeg":
return MediaType.get("image/jpeg");
case "png":
return MediaType.get("image/png");
case "tif":
case "tiff":
return MediaType.get("image/tiff");
case "gif":
return MediaType.get("image/gif");
case "pdf":
return MediaType.get("application/pdf");
case "docx":
return MediaType.get("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
case "doc":
return MediaType.get("application/msword");
case "xlsx":
return MediaType.get("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
case "xls":
return MediaType.get("application/vnd.ms-excel");
case "odt":
return MediaType.get("application/vnd.oasis.opendocument.text");
case "ods":
return MediaType.get("application/vnd.oasis.opendocument.spreadsheet");
case "txt":
return MediaType.get("text/plain");
default:
return null;
}
}
}
@@ -0,0 +1,72 @@
/**
* 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.fineract.integrationtests.newstyle;
import java.util.Optional;
import org.apache.fineract.client.models.GetClientsResponse;
import org.apache.fineract.client.models.PostClientsRequest;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
/**
* Integration Test for /clients API.
*
* @author Michael Vorburger.ch
*/
public class ClientTest extends IntegrationTest {
@Test
@Order(1)
void createOne() {
assertThat(create()).isGreaterThan(0);
}
@Test
@Order(2)
void retrieveOneExisting() {
assertThat(retrieveOne()).isPresent();
}
// The following are not tests, but helpful utilities for other tests
public Long getClientId() {
return retrieveOne().orElseGet(this::create);
}
Long create() {
// NB officeId(1) always exists (Head Office)
// TODO rm long cast, see https://issues.apache.org/jira/browse/FINERACT-1230
// TODO activationDate() why String? https://issues.apache.org/jira/browse/FINERACT-1232
// TODO why dateFormat and locale required even when no activationDate?!
// https://issues.apache.org/jira/browse/FINERACT-1233
return (long) ok(fineract().clients
.create5(new PostClientsRequest().officeId(1).fullname("TestClient").dateFormat(dateFormat()).locale("en_US")))
.getClientId();
}
Optional<Long> retrieveOne() {
GetClientsResponse clients = ok(
fineract().clients.retrieveAll20(null, null, null, null, null, null, null, null, 0, 1, null, null, false));
if (clients.getTotalFilteredRecords() > 0) {
// TODO rm long cast, see https://issues.apache.org/jira/browse/FINERACT-1230
return Optional.of((long) clients.getPageItems().get(0).getId());
}
return Optional.empty();
}
}
@@ -0,0 +1,115 @@
/**
* 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.fineract.integrationtests.newstyle;
import java.io.File;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.MultipartBody.Part;
import okhttp3.ResponseBody;
import org.apache.fineract.client.models.GetEntityTypeEntityIdDocumentsResponse;
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;
/**
* Integration Test for /documents API.
*
* @author Michael Vorburger.ch
*/
public class DocumentTest extends IntegrationTest {
final File testFile = new File(getClass().getResource("/michael.vorburger-crepes.jpg").getFile());
Long clientId = new ClientTest().getClientId();
Long documentId;
@Test
@Order(1)
void retrieveAllDocuments() throws IOException {
assertThat(ok(fineract().documents.retrieveAllDocuments("clients", clientId))).isNotNull();
}
@Test
@Order(2)
void createDocument() throws IOException {
String name = "Test";
Part part = Parts.fromFile(testFile);
String description = null;
// TODO used var in tests when moved from fineract-client (Java 8 only) to new module
PostEntityTypeEntityIdDocumentsResponse response = ok(
fineract().documents.createDocument("clients", clientId, part, name, description));
assertThat(response.getResourceId()).isNotNull();
assertThat(response.getResourceIdentifier()).isNotEmpty();
documentId = response.getResourceId();
}
@Test
@Order(3)
void getDocument() throws IOException {
GetEntityTypeEntityIdDocumentsResponse doc = ok(fineract().documents.getDocument("clients", clientId, documentId));
assertThat(doc.getName()).isEqualTo("Test");
assertThat(doc.getFileName()).isEqualTo(testFile.getName());
assertThat(doc.getDescription()).isNull();
assertThat(doc.getId()).isEqualTo(documentId);
assertThat(doc.getParentEntityType()).isEqualTo("clients");
assertThat(doc.getParentEntityId()).isEqualTo(clientId);
// TODO huh?! It's more than uploaded file; seems like a bug - it's including create body, not just file size
assertThat(doc.getSize()).isEqualTo(testFile.length() + 385);
// TODO huh?! MIME is always text/plain instead of image/jpeg... :(
assertThat(doc.getType()).isEqualTo("text/plain");
// TODO doc.getStorageType() shouldn't be exposed by the API?!
}
@Test
@Order(4)
void downloadFile() throws IOException {
ResponseBody r = ok(fineract().documents.downloadFile("clients", clientId, documentId));
assertThat(r.contentType()).isEqualTo(MediaType.get("text/plain")); // TODO wrong, bug; needs to be "image/jpeg"
// (as above)
assertThat(r.bytes().length).isEqualTo(testFile.length());
// NOK: assertThat(r.contentLength()).isEqualTo(testFile.length());
}
@Test
@Order(10)
void updateDocument() throws IOException {
String newName = "Test changed name";
String newDescription = getClass().getName();
ok(fineract().documents.updateDocument("clients", clientId, documentId, null, newName, newDescription));
GetEntityTypeEntityIdDocumentsResponse doc = ok(fineract().documents.getDocument("clients", clientId, documentId));
assertThat(doc.getName()).isEqualTo(newName);
assertThat(doc.getDescription()).isEqualTo(newDescription);
}
@Test
@Order(99)
void deleteDocument() throws IOException {
ok(fineract().documents.deleteDocument("clients", clientId, documentId));
assertThat(fineract().documents.getDocument("clients", clientId, documentId)).hasHttpStatus(404);
}
@Order(9999)
@Test // FINERACT-1036
void createDocumentBadArgs() throws IOException {
assertThat(fineract().documents.createDocument("clients", 123L, null, "test.pdf", null)).hasHttpStatus(400);
}
}
@@ -39,6 +39,8 @@ import org.apache.fineract.client.testutil.CallSubject;
import org.apache.fineract.client.util.Calls;
import org.apache.fineract.client.util.FineractClient;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import retrofit2.Call;
@@ -47,6 +49,8 @@ import retrofit2.Call;
*
* @author Michael Vorburger.ch
*/
// Allow keeping state between tests
@TestInstance(Lifecycle.PER_CLASS)
// TODO Remove @TestMethodOrder when https://github.com/junit-team/junit5/issues/1919 is available
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public abstract class IntegrationTest {
@@ -60,7 +64,7 @@ public abstract class IntegrationTest {
// TODO change from Fineract.dev to https://localhost:8443/fineract-provider/api/v1/ after FINERACT-1209
String url = System.getProperty("fineract.it.url", "https://demo.fineract.dev/fineract-provider/api/v1/");
fineract = FineractClient.builder().baseURL(url).tenant("default").basicAuth("mifos", "password").insecure(true)
.logging(Level.BODY).build();
.logging(Level.NONE).build();
}
return fineract;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

@@ -130,4 +130,9 @@ public final class ApiParameterError {
public List<ApiErrorMessageArg> getArgs() {
return this.args;
}
@Override
public String toString() {
return "ApiParameterError{developerMessage=" + developerMessage + "; ... }";
}
}
@@ -103,7 +103,7 @@ public class DocumentManagementApiResource {
+ "client_identifiers/1/documents\n" + "\n" + "loans/1/documents?fields=name,description")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DocumentManagementApiResourceSwagger.GetEntityTypeEntityIdDocumentsResponse.class)))) })
public String retreiveAllDocuments(@Context final UriInfo uriInfo,
public String retrieveAllDocuments(@Context final UriInfo uriInfo,
@PathParam("entityType") @Parameter(description = "entityType") final String entityType,
@PathParam("entityId") @Parameter(description = "entityId") final Long entityId) {