Add parameter to return the files when searching for a document (#582)

This commit is contained in:
Julien Kirch 2022-03-20 11:36:28 +01:00 committed by GitHub
parent 0b7c42e814
commit 64ec0f63ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 156 additions and 32 deletions

View File

@ -8,6 +8,8 @@ import com.sismics.util.context.ThreadLocalContext;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.NoResultException; import javax.persistence.NoResultException;
import javax.persistence.Query; import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -172,22 +174,33 @@ public class FileDao {
} }
/** /**
* Get files by document ID or all orphan files of an user. * Get files by document ID or all orphan files of a user.
* *
* @param userId User ID * @param userId User ID
* @param documentId Document ID * @param documentId Document ID
* @return List of files * @return List of files
*/ */
@SuppressWarnings("unchecked")
public List<File> getByDocumentId(String userId, String documentId) { public List<File> getByDocumentId(String userId, String documentId) {
EntityManager em = ThreadLocalContext.get().getEntityManager(); EntityManager em = ThreadLocalContext.get().getEntityManager();
if (documentId == null) { if (documentId == null) {
Query q = em.createQuery("select f from File f where f.documentId is null and f.deleteDate is null and f.latestVersion = true and f.userId = :userId order by f.createDate asc"); TypedQuery<File> q = em.createQuery("select f from File f where f.documentId is null and f.deleteDate is null and f.latestVersion = true and f.userId = :userId order by f.createDate asc", File.class);
q.setParameter("userId", userId); q.setParameter("userId", userId);
return q.getResultList(); return q.getResultList();
} else {
return getByDocumentsIds(Collections.singleton(documentId));
} }
Query q = em.createQuery("select f from File f where f.documentId = :documentId and f.latestVersion = true and f.deleteDate is null order by f.order asc"); }
q.setParameter("documentId", documentId);
/**
* Get files by documents IDs.
*
* @param documentIds Documents IDs
* @return List of files
*/
public List<File> getByDocumentsIds(Iterable<String> documentIds) {
EntityManager em = ThreadLocalContext.get().getEntityManager();
TypedQuery<File> q = em.createQuery("select f from File f where f.documentId in :documentIds and f.latestVersion = true and f.deleteDate is null order by f.order asc", File.class);
q.setParameter("documentIds", documentIds);
return q.getResultList(); return q.getResultList();
} }

View File

@ -0,0 +1,40 @@
package com.sismics.rest.util;
import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.util.DirectoryUtil;
import com.sismics.docs.core.util.FileUtil;
import com.sismics.rest.exception.ServerException;
import com.sismics.util.JsonUtil;
import javax.json.Json;
import javax.json.JsonObjectBuilder;
import java.io.IOException;
import java.nio.file.Files;
/**
* Rest utilities.
*
* @author bgamard
*/
public class RestUtil {
/**
* Transform a File into its JSON representation
* @param fileDb a file
* @return the JSON
*/
public static JsonObjectBuilder fileToJsonObjectBuilder(File fileDb) {
try {
return Json.createObjectBuilder()
.add("id", fileDb.getId())
.add("processing", FileUtil.isProcessingFile(fileDb.getId()))
.add("name", JsonUtil.nullable(fileDb.getName()))
.add("version", fileDb.getVersion())
.add("mimetype", fileDb.getMimeType())
.add("document_id", JsonUtil.nullable(fileDb.getDocumentId()))
.add("create_date", fileDb.getCreateDate().getTime())
.add("size", Files.size(DirectoryUtil.getStorageDirectory().resolve(fileDb.getId())));
} catch (IOException e) {
throw new ServerException("FileError", "Unable to get the size of " + fileDb.getId(), e);
}
}
}

View File

@ -27,11 +27,13 @@ import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException; import com.sismics.rest.exception.ServerException;
import com.sismics.rest.util.AclUtil; import com.sismics.rest.util.AclUtil;
import com.sismics.rest.util.RestUtil;
import com.sismics.rest.util.ValidationUtil; import com.sismics.rest.util.ValidationUtil;
import com.sismics.util.EmailUtil; import com.sismics.util.EmailUtil;
import com.sismics.util.JsonUtil; import com.sismics.util.JsonUtil;
import com.sismics.util.context.ThreadLocalContext; import com.sismics.util.context.ThreadLocalContext;
import com.sismics.util.mime.MimeType; import com.sismics.util.mime.MimeType;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
@ -73,6 +75,7 @@ public class DocumentResource extends BaseResource {
* @apiGroup Document * @apiGroup Document
* @apiParam {String} id Document ID * @apiParam {String} id Document ID
* @apiParam {String} share Share ID * @apiParam {String} share Share ID
* @apiParam {Booleans} files If true includes files information
* @apiSuccess {String} id ID * @apiSuccess {String} id ID
* @apiSuccess {String} title Title * @apiSuccess {String} title Title
* @apiSuccess {String} description Description * @apiSuccess {String} description Description
@ -119,6 +122,12 @@ public class DocumentResource extends BaseResource {
* @apiSuccess {String} route_step.name Route step name * @apiSuccess {String} route_step.name Route step name
* @apiSuccess {String="APPROVE", "VALIDATE"} route_step.type Route step type * @apiSuccess {String="APPROVE", "VALIDATE"} route_step.type Route step type
* @apiSuccess {Boolean} route_step.transitionable True if the route step is actionable by the current user * @apiSuccess {Boolean} route_step.transitionable True if the route step is actionable by the current user
* @apiSuccess {Object[]} files List of files
* @apiSuccess {String} files.id ID
* @apiSuccess {String} files.name File name
* @apiSuccess {String} files.version Zero-based version number
* @apiSuccess {String} files.mimetype MIME type
* @apiSuccess {String} files.create_date Create date (timestamp)
* @apiError (client) NotFound Document not found * @apiError (client) NotFound Document not found
* @apiPermission none * @apiPermission none
* @apiVersion 1.5.0 * @apiVersion 1.5.0
@ -131,7 +140,8 @@ public class DocumentResource extends BaseResource {
@Path("{id: [a-z0-9\\-]+}") @Path("{id: [a-z0-9\\-]+}")
public Response get( public Response get(
@PathParam("id") String documentId, @PathParam("id") String documentId,
@QueryParam("share") String shareId) { @QueryParam("share") String shareId,
@QueryParam("files") Boolean files) {
authenticate(); authenticate();
DocumentDao documentDao = new DocumentDao(); DocumentDao documentDao = new DocumentDao();
@ -240,6 +250,19 @@ public class DocumentResource extends BaseResource {
// Add custom metadata // Add custom metadata
MetadataUtil.addMetadata(document, documentId); MetadataUtil.addMetadata(document, documentId);
// Add files
if (Boolean.TRUE == files) {
FileDao fileDao = new FileDao();
List<File> fileList = fileDao.getByDocumentsIds(Collections.singleton(documentId));
JsonArrayBuilder filesArrayBuilder = Json.createArrayBuilder();
for (File fileDb : fileList) {
filesArrayBuilder.add(RestUtil.fileToJsonObjectBuilder(fileDb));
}
document.add("files", filesArrayBuilder);
}
return Response.ok().entity(document.build()).build(); return Response.ok().entity(document.build()).build();
} }
@ -327,6 +350,7 @@ public class DocumentResource extends BaseResource {
* @apiParam {Number} sort_column Column index to sort on * @apiParam {Number} sort_column Column index to sort on
* @apiParam {Boolean} asc If true, sort in ascending order * @apiParam {Boolean} asc If true, sort in ascending order
* @apiParam {String} search Search query * @apiParam {String} search Search query
* @apiParam {Booleans} files If true includes files information
* @apiSuccess {Number} total Total number of documents * @apiSuccess {Number} total Total number of documents
* @apiSuccess {Object[]} documents List of documents * @apiSuccess {Object[]} documents List of documents
* @apiSuccess {String} documents.id ID * @apiSuccess {String} documents.id ID
@ -345,6 +369,12 @@ public class DocumentResource extends BaseResource {
* @apiSuccess {String} documents.tags.id ID * @apiSuccess {String} documents.tags.id ID
* @apiSuccess {String} documents.tags.name Name * @apiSuccess {String} documents.tags.name Name
* @apiSuccess {String} documents.tags.color Color * @apiSuccess {String} documents.tags.color Color
* @apiSuccess {Object[]} documents.files List of files
* @apiSuccess {String} documents.files.id ID
* @apiSuccess {String} documents.files.name File name
* @apiSuccess {String} documents.files.version Zero-based version number
* @apiSuccess {String} documents.files.mimetype MIME type
* @apiSuccess {String} documents.files.create_date Create date (timestamp)
* @apiSuccess {String[]} suggestions List of search suggestions * @apiSuccess {String[]} suggestions List of search suggestions
* @apiError (client) ForbiddenError Access denied * @apiError (client) ForbiddenError Access denied
* @apiError (server) SearchError Error searching in documents * @apiError (server) SearchError Error searching in documents
@ -356,6 +386,7 @@ public class DocumentResource extends BaseResource {
* @param sortColumn Sort column * @param sortColumn Sort column
* @param asc Sorting * @param asc Sorting
* @param search Search query * @param search Search query
* @param files Files list
* @return Response * @return Response
*/ */
@GET @GET
@ -365,7 +396,8 @@ public class DocumentResource extends BaseResource {
@QueryParam("offset") Integer offset, @QueryParam("offset") Integer offset,
@QueryParam("sort_column") Integer sortColumn, @QueryParam("sort_column") Integer sortColumn,
@QueryParam("asc") Boolean asc, @QueryParam("asc") Boolean asc,
@QueryParam("search") String search) { @QueryParam("search") String search,
@QueryParam("files") Boolean files) {
if (!authenticate()) { if (!authenticate()) {
throw new ForbiddenClientException(); throw new ForbiddenClientException();
} }
@ -385,6 +417,14 @@ public class DocumentResource extends BaseResource {
throw new ServerException("SearchError", "Error searching in documents", e); throw new ServerException("SearchError", "Error searching in documents", e);
} }
// Find the files of the documents
List<File> filesList = null;
if (Boolean.TRUE == files) {
Iterable<String> documentsIds = CollectionUtils.collect(paginatedList.getResultList(), DocumentDto::getId);
FileDao fileDao = new FileDao();
filesList = fileDao.getByDocumentsIds(documentsIds);
}
for (DocumentDto documentDto : paginatedList.getResultList()) { for (DocumentDto documentDto : paginatedList.getResultList()) {
// Get tags accessible by the current user on this document // Get tags accessible by the current user on this document
List<TagDto> tagDtoList = tagDao.findByCriteria(new TagCriteria() List<TagDto> tagDtoList = tagDao.findByCriteria(new TagCriteria()
@ -398,7 +438,7 @@ public class DocumentResource extends BaseResource {
.add("color", tagDto.getColor())); .add("color", tagDto.getColor()));
} }
documents.add(Json.createObjectBuilder() JsonObjectBuilder documentObjectBuilder = Json.createObjectBuilder()
.add("id", documentDto.getId()) .add("id", documentDto.getId())
.add("highlight", JsonUtil.nullable(documentDto.getHighlight())) .add("highlight", JsonUtil.nullable(documentDto.getHighlight()))
.add("file_id", JsonUtil.nullable(documentDto.getFileId())) .add("file_id", JsonUtil.nullable(documentDto.getFileId()))
@ -411,7 +451,17 @@ public class DocumentResource extends BaseResource {
.add("active_route", documentDto.isActiveRoute()) .add("active_route", documentDto.isActiveRoute())
.add("current_step_name", JsonUtil.nullable(documentDto.getCurrentStepName())) .add("current_step_name", JsonUtil.nullable(documentDto.getCurrentStepName()))
.add("file_count", documentDto.getFileCount()) .add("file_count", documentDto.getFileCount())
.add("tags", tags)); .add("tags", tags);
if (Boolean.TRUE == files) {
JsonArrayBuilder filesArrayBuilder = Json.createArrayBuilder();
// Find files matching the document
Collection<File> filesOfDocument = CollectionUtils.select(filesList, file -> file.getDocumentId().equals(documentDto.getId()));
for (File fileDb : filesOfDocument) {
filesArrayBuilder.add(RestUtil.fileToJsonObjectBuilder(fileDb));
}
documentObjectBuilder.add("files", filesArrayBuilder);
}
documents.add(documentObjectBuilder);
} }
JsonArrayBuilder suggestions = Json.createArrayBuilder(); JsonArrayBuilder suggestions = Json.createArrayBuilder();

View File

@ -21,6 +21,7 @@ import com.sismics.docs.core.util.FileUtil;
import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException; import com.sismics.rest.exception.ServerException;
import com.sismics.rest.util.RestUtil;
import com.sismics.rest.util.ValidationUtil; import com.sismics.rest.util.ValidationUtil;
import com.sismics.util.HttpUtil; import com.sismics.util.HttpUtil;
import com.sismics.util.JsonUtil; import com.sismics.util.JsonUtil;
@ -427,27 +428,13 @@ public class FileResource extends BaseResource {
} }
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
List<File> fileList = fileDao.getByDocumentId(principal.getId(), documentId);
JsonArrayBuilder files = Json.createArrayBuilder(); JsonArrayBuilder files = Json.createArrayBuilder();
for (File fileDb : fileList) { for (File fileDb : fileDao.getByDocumentId(principal.getId(), documentId)) {
try { files.add(RestUtil.fileToJsonObjectBuilder(fileDb));
files.add(Json.createObjectBuilder()
.add("id", fileDb.getId())
.add("processing", FileUtil.isProcessingFile(fileDb.getId()))
.add("name", JsonUtil.nullable(fileDb.getName()))
.add("version", fileDb.getVersion())
.add("mimetype", fileDb.getMimeType())
.add("document_id", JsonUtil.nullable(fileDb.getDocumentId()))
.add("create_date", fileDb.getCreateDate().getTime())
.add("size", Files.size(DirectoryUtil.getStorageDirectory().resolve(fileDb.getId()))));
} catch (IOException e) {
throw new ServerException("FileError", "Unable to get the size of " + fileDb.getId(), e);
} }
}
JsonObjectBuilder response = Json.createObjectBuilder() JsonObjectBuilder response = Json.createObjectBuilder()
.add("files", files); .add("files", files);
return Response.ok().entity(response.build()).build(); return Response.ok().entity(response.build()).build();
} }

View File

@ -264,6 +264,7 @@ public class TestDocumentResource extends BaseJerseyTest {
Assert.assertEquals(document2Id, relations.getJsonObject(0).getString("id")); Assert.assertEquals(document2Id, relations.getJsonObject(0).getString("id"));
Assert.assertFalse(relations.getJsonObject(0).getBoolean("source")); Assert.assertFalse(relations.getJsonObject(0).getBoolean("source"));
Assert.assertEquals("My super title document 2", relations.getJsonObject(0).getString("title")); Assert.assertEquals("My super title document 2", relations.getJsonObject(0).getString("title"));
Assert.assertFalse(json.containsKey("files"));
// Get document 2 // Get document 2
json = target().path("/document/" + document2Id).request() json = target().path("/document/" + document2Id).request()
@ -275,6 +276,7 @@ public class TestDocumentResource extends BaseJerseyTest {
Assert.assertEquals(document1Id, relations.getJsonObject(0).getString("id")); Assert.assertEquals(document1Id, relations.getJsonObject(0).getString("id"));
Assert.assertTrue(relations.getJsonObject(0).getBoolean("source")); Assert.assertTrue(relations.getJsonObject(0).getBoolean("source"));
Assert.assertEquals("My super title document 1", relations.getJsonObject(0).getString("title")); Assert.assertEquals("My super title document 1", relations.getJsonObject(0).getString("title"));
Assert.assertFalse(json.containsKey("files"));
// Create a tag // Create a tag
json = target().path("/tag").request() json = target().path("/tag").request()
@ -330,6 +332,25 @@ public class TestDocumentResource extends BaseJerseyTest {
.get(JsonObject.class); .get(JsonObject.class);
documents = json.getJsonArray("documents"); documents = json.getJsonArray("documents");
Assert.assertEquals(1, documents.size()); Assert.assertEquals(1, documents.size());
Assert.assertEquals(document1Id, documents.getJsonObject(0).getString("id"));
Assert.assertFalse(documents.getJsonObject(0).containsKey("files"));
// Search documents by query with files
json = target().path("/document/list")
.queryParam("files", true)
.queryParam("search", "new")
.request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, document1Token)
.get(JsonObject.class);
documents = json.getJsonArray("documents");
Assert.assertEquals(1, documents.size());
Assert.assertEquals(1, documents.size());
Assert.assertEquals(document1Id, documents.getJsonObject(0).getString("id"));
JsonArray files = documents.getJsonObject(0).getJsonArray("files");
Assert.assertEquals(1, files.size());
Assert.assertEquals(file1Id, files.getJsonObject(0).getString("id"));
Assert.assertEquals("Einstein-Roosevelt-letter.png", files.getJsonObject(0).getString("name"));
Assert.assertEquals("image/png", files.getJsonObject(0).getString("mimetype"));
// Get document 1 // Get document 1
json = target().path("/document/" + document1Id).request() json = target().path("/document/" + document1Id).request()
@ -353,6 +374,19 @@ public class TestDocumentResource extends BaseJerseyTest {
Assert.assertEquals("document1", contributors.getJsonObject(0).getString("username")); Assert.assertEquals("document1", contributors.getJsonObject(0).getString("username"));
relations = json.getJsonArray("relations"); relations = json.getJsonArray("relations");
Assert.assertEquals(0, relations.size()); Assert.assertEquals(0, relations.size());
Assert.assertFalse(json.containsKey("files"));
// Get document 1 with its files
json = target().path("/document/" + document1Id)
.queryParam("files", true)
.request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, document1Token)
.get(JsonObject.class);
files = json.getJsonArray("files");
Assert.assertEquals(1, files.size());
Assert.assertEquals(file1Id, files.getJsonObject(0).getString("id"));
Assert.assertEquals("Einstein-Roosevelt-letter.png", files.getJsonObject(0).getString("name"));
Assert.assertEquals("image/png", files.getJsonObject(0).getString("mimetype"));
// Get document 2 // Get document 2
json = target().path("/document/" + document1Id).request() json = target().path("/document/" + document1Id).request()