1
0
mirror of https://github.com/sismics/docs.git synced 2025-12-13 17:56:20 +00:00

#32: Comments system (server side)

This commit is contained in:
jendib
2015-11-16 02:22:51 +01:00
parent b3e44b84d2
commit 97252bb5da
30 changed files with 743 additions and 124 deletions

View File

@@ -1,3 +1,3 @@
api.current_version=${project.version}
api.min_version=1.0
db.version=2
db.version=3

View File

@@ -0,0 +1,150 @@
package com.sismics.docs.rest.resource;
import java.util.List;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.CommentDao;
import com.sismics.docs.core.dao.jpa.DocumentDao;
import com.sismics.docs.core.dao.jpa.dto.CommentDto;
import com.sismics.docs.core.model.jpa.Comment;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.util.ValidationUtil;
/**
* Comment REST resource.
*
* @author bgamard
*/
@Path("/comment")
public class CommentResource extends BaseResource {
/**
* Add a comment.
*
* @param documentId Document ID
* @param content Comment content
* @return Response
*/
@PUT
public Response add(@FormParam("id") String documentId,
@FormParam("content") String content) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Validate input data
ValidationUtil.validateRequired(documentId, "id");
content = ValidationUtil.validateLength(content, "content", 1, 4000, false);
// Read access on doc gives access to write comments
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(documentId, PermType.READ, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Create the comment
Comment comment = new Comment();
comment.setDocumentId(documentId);
comment.setContent(content);
comment.setUserId(principal.getId());
CommentDao commentDao = new CommentDao();
commentDao.create(comment);
// Returns the comment
JsonObjectBuilder response = Json.createObjectBuilder()
.add("id", comment.getId())
.add("creator", principal.getName())
.add("content", comment.getContent())
.add("create_date", comment.getCreateDate().getTime());
return Response.ok().entity(response.build()).build();
}
/**
* Delete a comment.
*
* @param id Comment ID
* @return Response
*/
@DELETE
@Path("{id: [a-z0-9\\-]+}")
public Response delete(@PathParam("id") String id) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Validate input data
ValidationUtil.validateRequired(id, "id");
// Get the comment
CommentDao commentDao = new CommentDao();
Comment comment = commentDao.getActiveById(id);
if (comment == null) {
return Response.status(Status.NOT_FOUND).build();
}
// If the current user owns the comment, skip ACL check
if (!comment.getUserId().equals(principal.getId())) {
// Get the associated document
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(comment.getDocumentId(), PermType.WRITE, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
}
// Delete the comment
commentDao.delete(id);
// Always return OK
JsonObjectBuilder response = Json.createObjectBuilder()
.add("status", "ok");
return Response.ok().entity(response.build()).build();
}
/**
* Get all comments on a document.
*
* @param documentId DocumentID
* @return Response
*/
@GET
@Path("{documentId: [a-z0-9\\-]+}")
public Response get(@PathParam("documentId") String documentId) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
// Read access on doc gives access to read comments
DocumentDao documentDao = new DocumentDao();
if (documentDao.getDocument(documentId, PermType.READ, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Assemble results
CommentDao commentDao = new CommentDao();
List<CommentDto> commentDtoList = commentDao.getByDocumentId(documentId);
JsonArrayBuilder comments = Json.createArrayBuilder();
for (CommentDto commentDto : commentDtoList) {
comments.add(Json.createObjectBuilder()
.add("id", commentDto.getId())
.add("content", commentDto.getContent())
.add("creator", commentDto.getCreatorName())
.add("create_date", commentDto.getCreateTimestamp()));
}
// Always return OK
JsonObjectBuilder response = Json.createObjectBuilder()
.add("comments", comments);
return Response.ok().entity(response.build()).build();
}
}

View File

@@ -11,7 +11,6 @@ import java.util.UUID;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.persistence.NoResultException;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
@@ -82,17 +81,15 @@ public class DocumentResource extends BaseResource {
DocumentDao documentDao = new DocumentDao();
AclDao aclDao = new AclDao();
DocumentDto documentDto;
try {
documentDto = documentDao.getDocument(documentId);
// Check document visibility
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
} catch (NoResultException e) {
DocumentDto documentDto = documentDao.getDocument(documentId);
if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Check document visibility
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
JsonObjectBuilder document = Json.createObjectBuilder()
.add("id", documentDto.getId())
@@ -415,9 +412,8 @@ public class DocumentResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
Document document;
try {
document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
} catch (NoResultException e) {
document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
if (document == null) {
return Response.status(Status.NOT_FOUND).build();
}
@@ -492,12 +488,9 @@ public class DocumentResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
FileDao fileDao = new FileDao();
Document document;
List<File> fileList;
try {
document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
fileList = fileDao.getByDocumentId(principal.getId(), id);
} catch (NoResultException e) {
Document document = documentDao.getDocument(id, PermType.WRITE, principal.getId());
List<File> fileList = fileDao.getByDocumentId(principal.getId(), id);
if (document == null) {
return Response.status(Status.NOT_FOUND).build();
}

View File

@@ -16,7 +16,6 @@ import java.util.zip.ZipOutputStream;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.persistence.NoResultException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
@@ -98,9 +97,8 @@ public class FileResource extends BaseResource {
documentId = null;
} else {
DocumentDao documentDao = new DocumentDao();
try {
document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
} catch (NoResultException e) {
document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
if (document == null) {
return Response.status(Status.NOT_FOUND).build();
}
}
@@ -190,12 +188,9 @@ public class FileResource extends BaseResource {
// Get the document and the file
DocumentDao documentDao = new DocumentDao();
FileDao fileDao = new FileDao();
Document document;
File file;
try {
file = fileDao.getFile(id, principal.getId());
document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
} catch (NoResultException e) {
File file = fileDao.getFile(id, principal.getId());
Document document = documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
if (file == null || document == null) {
return Response.status(Status.NOT_FOUND).build();
}
@@ -251,9 +246,7 @@ public class FileResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
try {
documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
} catch (NoResultException e) {
if (documentDao.getDocument(documentId, PermType.WRITE, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
@@ -330,19 +323,18 @@ public class FileResource extends BaseResource {
// Get the file
FileDao fileDao = new FileDao();
DocumentDao documentDao = new DocumentDao();
File file;
try {
file = fileDao.getFile(id);
if (file.getDocumentId() == null) {
// It's an orphan file
if (!file.getUserId().equals(principal.getId())) {
// But not ours
throw new ForbiddenClientException();
}
} else {
documentDao.getDocument(file.getDocumentId(), PermType.WRITE, principal.getId());
File file = fileDao.getFile(id);
if (file == null) {
return Response.status(Status.NOT_FOUND).build();
}
if (file.getDocumentId() == null) {
// It's an orphan file
if (!file.getUserId().equals(principal.getId())) {
// But not ours
throw new ForbiddenClientException();
}
} catch (NoResultException e) {
} else if (documentDao.getDocument(file.getDocumentId(), PermType.WRITE, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
@@ -383,26 +375,24 @@ public class FileResource extends BaseResource {
// Get the file
FileDao fileDao = new FileDao();
UserDao userDao = new UserDao();
File file;
try {
file = fileDao.getFile(fileId);
if (file.getDocumentId() == null) {
// It's an orphan file
if (!file.getUserId().equals(principal.getId())) {
// But not ours
throw new ForbiddenClientException();
}
} else {
// Check document accessibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(file.getDocumentId(), PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
}
} catch (NoResultException e) {
File file = fileDao.getFile(fileId);
if (file == null) {
return Response.status(Status.NOT_FOUND).build();
}
if (file.getDocumentId() == null) {
// It's an orphan file
if (!file.getUserId().equals(principal.getId())) {
// But not ours
throw new ForbiddenClientException();
}
} else {
// Check document accessibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(file.getDocumentId(), PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
}
// Get the stored file
@@ -470,19 +460,17 @@ public class FileResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
DocumentDto documentDto;
try {
documentDto = documentDao.getDocument(documentId);
// Check document visibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
} catch (NoResultException e) {
DocumentDto documentDto = documentDao.getDocument(documentId);
if (documentDto == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Check document visibility
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(documentId, PermType.READ, shareId == null ? principal.getId() : shareId)) {
throw new ForbiddenClientException();
}
// Get files and user associated with this document
FileDao fileDao = new FileDao();
final UserDao userDao = new UserDao();

View File

@@ -6,13 +6,13 @@ import java.util.List;
import javax.json.Json;
import javax.json.JsonObjectBuilder;
import javax.persistence.NoResultException;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.sismics.docs.core.constant.AclTargetType;
import com.sismics.docs.core.constant.PermType;
@@ -53,10 +53,8 @@ public class ShareResource extends BaseResource {
// Get the document
DocumentDao documentDao = new DocumentDao();
try {
documentDao.getDocument(documentId, PermType.WRITE, principal.getId());
} catch (NoResultException e) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId));
if (documentDao.getDocument(documentId, PermType.WRITE, principal.getId()) == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Create the share

View File

@@ -17,6 +17,9 @@
<span ng-switch-when="File">
<a ng-href="#/document/view/{{ log.message }}/file/{{ log.target }}">Open</a>
</span>
<span ng-switch-when="Comment">
<a ng-href="#/document/view/{{ log.message }}/comments">See</a>
</span>
<span ng-switch-when="Acl">
{{ log.message }}
</span>

View File

@@ -1,3 +1,3 @@
api.current_version=${project.version}
api.min_version=1.0
db.version=2
db.version=3

View File

@@ -1,3 +1,3 @@
api.current_version=${project.version}
api.min_version=1.0
db.version=2
db.version=3

View File

@@ -12,7 +12,6 @@ import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter;
/**
* Test the audit log resource.
*

View File

@@ -0,0 +1,143 @@
package com.sismics.docs.rest;
import java.util.Date;
import javax.json.JsonObject;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.junit.Assert;
import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter;
/**
* Exhaustive test of the comment resource.
*
* @author bgamard
*/
public class TestCommentResource extends BaseJerseyTest {
/**
* Test the comment resource.
*
* @throws Exception
*/
@Test
public void testCommentResource() throws Exception {
// Login comment1
clientUtil.createUser("comment1");
String comment1Token = clientUtil.login("comment1");
// Login comment2
clientUtil.createUser("comment2");
String comment2Token = clientUtil.login("comment2");
// Create a document with comment1
long create1Date = new Date().getTime();
JsonObject json = target().path("/document").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.put(Entity.form(new Form()
.param("title", "My super title document 1")
.param("description", "My super description for document 1")
.param("language", "eng")
.param("create_date", Long.toString(create1Date))), JsonObject.class);
String document1Id = json.getString("id");
Assert.assertNotNull(document1Id);
// Create a comment with comment2 (fail, no read access)
Response response = target().path("/comment").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.put(Entity.form(new Form()
.param("id", document1Id)
.param("content", "Comment by comment2")));
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
// Read comments with comment2 (fail, no read access)
response = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.get();
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
// Read comments with comment 1
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.get(JsonObject.class);
Assert.assertEquals(0, json.getJsonArray("comments").size());
// Create a comment with comment1
json = target().path("/comment").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.put(Entity.form(new Form()
.param("id", document1Id)
.param("content", "Comment by comment1")), JsonObject.class);
String comment1Id = json.getString("id");
Assert.assertNotNull(comment1Id);
Assert.assertEquals("Comment by comment1", json.getString("content"));
Assert.assertEquals("comment1", json.getString("creator"));
Assert.assertNotNull(json.getJsonNumber("create_date"));
// Read comments with comment1
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.get(JsonObject.class);
Assert.assertEquals(1, json.getJsonArray("comments").size());
Assert.assertEquals(comment1Id, json.getJsonArray("comments").getJsonObject(0).getString("id"));
// Delete a comment with comment2 (fail, no write access)
response = target().path("/comment/" + comment1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.delete();
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
// Delete a comment with comment1
json = target().path("/comment/" + comment1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.delete(JsonObject.class);
// Read comments with comment1
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.get(JsonObject.class);
Assert.assertEquals(0, json.getJsonArray("comments").size());
// Add an ACL READ for comment2 with comment1
json = target().path("/acl").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment1Token)
.put(Entity.form(new Form()
.param("source", document1Id)
.param("perm", "READ")
.param("username", "comment2")), JsonObject.class);
// Create a comment with comment2
json = target().path("/comment").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.put(Entity.form(new Form()
.param("id", document1Id)
.param("content", "Comment by comment2")), JsonObject.class);
String comment2Id = json.getString("id");
// Read comments with comment2
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.get(JsonObject.class);
Assert.assertEquals(1, json.getJsonArray("comments").size());
JsonObject comment = json.getJsonArray("comments").getJsonObject(0);
Assert.assertEquals(comment2Id, comment.getString("id"));
Assert.assertEquals("Comment by comment2", comment.getString("content"));
Assert.assertEquals("comment2", comment.getString("creator"));
Assert.assertNotNull(comment.getJsonNumber("create_date"));
// Delete a comment with comment2
json = target().path("/comment/" + comment2Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.delete(JsonObject.class);
// Read comments with comment2
json = target().path("/comment/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, comment2Token)
.get(JsonObject.class);
Assert.assertEquals(0, json.getJsonArray("comments").size());
}
}

View File

@@ -26,8 +26,6 @@ import com.sismics.util.filter.TokenBasedSecurityFilter;
import com.sismics.util.mime.MimeType;
import com.sismics.util.mime.MimeTypeUtil;
/**
* Exhaustive test of the document resource.
*

View File

@@ -27,7 +27,6 @@ import com.sismics.util.filter.TokenBasedSecurityFilter;
import com.sismics.util.mime.MimeType;
import com.sismics.util.mime.MimeTypeUtil;
/**
* Exhaustive test of the file resource.
*

View File

@@ -20,7 +20,6 @@ import com.google.common.io.ByteStreams;
import com.google.common.io.Resources;
import com.sismics.util.filter.TokenBasedSecurityFilter;
/**
* Exhaustive test of the share resource.
*

View File

@@ -13,7 +13,6 @@ import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter;
/**
* Test the tag resource.
*

View File

@@ -14,7 +14,6 @@ import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter;
/**
* Exhaustive test of the user resource.
*