mirror of
https://github.com/sismics/docs.git
synced 2024-11-25 07:07:57 +01:00
#159: return the active step in GET /document/id
This commit is contained in:
parent
5e713f0c2a
commit
503cfff82e
@ -217,7 +217,7 @@ public class DocumentDao {
|
||||
if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) {
|
||||
LuceneDao luceneDao = new LuceneDao();
|
||||
Set<String> documentIdList = luceneDao.search(criteria.getSearch(), criteria.getFullSearch());
|
||||
if (documentIdList.size() == 0) {
|
||||
if (documentIdList.isEmpty()) {
|
||||
// If the search doesn't find any document, the request should return nothing
|
||||
documentIdList.add(UUID.randomUUID().toString());
|
||||
}
|
||||
|
@ -6,7 +6,9 @@ import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -36,4 +38,12 @@ public class RouteDao {
|
||||
|
||||
return route.getId();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Route> getActiveRoutes(String documentId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("from Route r where r.documentId = :documentId and r.deleteDate is null order by r.createDate desc");
|
||||
q.setParameter("documentId", documentId);
|
||||
return q.getResultList();
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ import com.sismics.docs.core.model.jpa.RouteStep;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -30,4 +32,12 @@ public class RouteStepDao {
|
||||
|
||||
return routeStep.getId();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<RouteStep> getRouteSteps(String routeId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("from RouteStep r where r.routeId = :routeId and r.deleteDate is null order by r.order asc");
|
||||
q.setParameter("routeId", routeId);
|
||||
return q.getResultList();
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,11 @@
|
||||
package com.sismics.docs.core.model.jpa;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* ACL entity.
|
||||
*
|
||||
@ -34,6 +28,8 @@ public class Acl implements Loggable {
|
||||
@Enumerated(EnumType.STRING)
|
||||
private PermType perm;
|
||||
|
||||
// TODO Add ACL type enum: USER, ROUTING
|
||||
|
||||
/**
|
||||
* ACL source ID.
|
||||
*/
|
||||
|
@ -24,7 +24,6 @@ import java.security.Security;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class EncryptionUtil {
|
||||
|
||||
/**
|
||||
* Salt.
|
||||
*/
|
||||
|
@ -1,17 +0,0 @@
|
||||
package com.sismics.docs.core.util;
|
||||
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
/**
|
||||
* Entity manager utils.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class EntityManagerUtil {
|
||||
/**
|
||||
* Flush the entity manager session.
|
||||
*/
|
||||
public static void flush() {
|
||||
ThreadLocalContext.get().getEntityManager().flush();
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.sismics.docs.core.util;
|
||||
|
||||
import com.sismics.docs.core.dao.jpa.RouteDao;
|
||||
import com.sismics.docs.core.dao.jpa.RouteStepDao;
|
||||
import com.sismics.docs.core.model.jpa.Route;
|
||||
import com.sismics.docs.core.model.jpa.RouteStep;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Routing utilities.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class RoutingUtil {
|
||||
/**
|
||||
* Get the current route step from a document.
|
||||
*
|
||||
* @param documentId Document ID
|
||||
* @return Active route step
|
||||
*/
|
||||
public static RouteStep getCurrentStep(String documentId) {
|
||||
// TODO Optimize
|
||||
RouteDao routeDao = new RouteDao();
|
||||
List<Route> routeList = routeDao.getActiveRoutes(documentId);
|
||||
if (routeList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Route route = routeList.get(0);
|
||||
RouteStepDao routeStepDao = new RouteStepDao();
|
||||
List<RouteStep> routeStepList = routeStepDao.getRouteSteps(route.getId());
|
||||
for (RouteStep routeStep : routeStepList) {
|
||||
if (routeStep.getEndDate() == null) {
|
||||
return routeStep;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.sismics.docs.core.util;
|
||||
|
||||
import com.sismics.docs.core.constant.AclTargetType;
|
||||
import com.sismics.docs.core.dao.jpa.GroupDao;
|
||||
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||
import com.sismics.docs.core.model.jpa.Group;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
|
||||
/**
|
||||
* Security utilities.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class SecurityUtil {
|
||||
/**
|
||||
* Get an ACL target ID from an object name and type.
|
||||
*
|
||||
* @param name Object name
|
||||
* @param type Object type
|
||||
* @return Target ID
|
||||
*/
|
||||
public static String getTargetIdFromName(String name, AclTargetType type) {
|
||||
switch (type) {
|
||||
case USER:
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(name);
|
||||
if (user != null) {
|
||||
return user.getId();
|
||||
}
|
||||
case GROUP:
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(name);
|
||||
if (group != null) {
|
||||
return group.getId();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package com.sismics.docs.core.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PushbackInputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Stream utilities.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class StreamUtil {
|
||||
|
||||
/**
|
||||
* Detects if the stream is gzipped, and returns a uncompressed stream according to this.
|
||||
*
|
||||
* @param is InputStream
|
||||
* @return InputStream
|
||||
* @throws IOException
|
||||
*/
|
||||
public static InputStream detectGzip(InputStream is) throws IOException {
|
||||
PushbackInputStream pb = new PushbackInputStream(is, 2);
|
||||
byte [] signature = new byte[2];
|
||||
pb.read(signature);
|
||||
pb.unread(signature);
|
||||
if(signature[0] == (byte) GZIPInputStream.GZIP_MAGIC && signature[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)) {
|
||||
return new GZIPInputStream(pb);
|
||||
} else {
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,10 @@ import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.GroupDto;
|
||||
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||
import com.sismics.docs.core.model.jpa.*;
|
||||
import com.sismics.docs.core.model.jpa.Acl;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.Tag;
|
||||
import com.sismics.docs.core.util.SecurityUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
@ -70,29 +73,8 @@ public class AclResource extends BaseResource {
|
||||
AclTargetType type = AclTargetType.valueOf(ValidationUtil.validateLength(typeStr, "type", 1, 10, false));
|
||||
targetName = ValidationUtil.validateLength(targetName, "target", 1, 50, false);
|
||||
|
||||
// Search user or group
|
||||
String targetId = null;
|
||||
switch (type) {
|
||||
case USER:
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(targetName);
|
||||
if (user != null) {
|
||||
targetId = user.getId();
|
||||
}
|
||||
break;
|
||||
case GROUP:
|
||||
GroupDao groupDao = new GroupDao();
|
||||
Group group = groupDao.getActiveByName(targetName);
|
||||
if (group != null) {
|
||||
targetId = group.getId();
|
||||
}
|
||||
break;
|
||||
case SHARE:
|
||||
// Share must use the Share REST resource
|
||||
break;
|
||||
}
|
||||
|
||||
// Does a target has been found?
|
||||
String targetId = SecurityUtil.getTargetIdFromName(targetName, type);
|
||||
if (targetId == null) {
|
||||
throw new ClientException("InvalidTarget", MessageFormat.format("This target does not exist: {0}", targetName));
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.sismics.docs.rest.resource;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
import com.sismics.docs.core.dao.jpa.*;
|
||||
@ -13,11 +12,9 @@ import com.sismics.docs.core.event.DocumentCreatedAsyncEvent;
|
||||
import com.sismics.docs.core.event.DocumentDeletedAsyncEvent;
|
||||
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
|
||||
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
|
||||
import com.sismics.docs.core.model.jpa.Acl;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.model.jpa.*;
|
||||
import com.sismics.docs.core.util.PdfUtil;
|
||||
import com.sismics.docs.core.util.RoutingUtil;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
@ -42,7 +39,6 @@ import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
@ -102,6 +98,10 @@ public class DocumentResource extends BaseResource {
|
||||
* @apiSuccess {String} relations.id ID
|
||||
* @apiSuccess {String} relations.title Title
|
||||
* @apiSuccess {String} relations.source True if this document is the source of the relation
|
||||
* @apiSuccess {Object} route_step The current active route step
|
||||
* @apiSuccess {String} route_step.name Route step name
|
||||
* @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
|
||||
* @apiError (client) NotFound Document not found
|
||||
* @apiPermission none
|
||||
* @apiVersion 1.5.0
|
||||
@ -210,6 +210,15 @@ public class DocumentResource extends BaseResource {
|
||||
}
|
||||
document.add("relations", relationList);
|
||||
|
||||
// Add current route step
|
||||
RouteStep routeStep = RoutingUtil.getCurrentStep(documentId);
|
||||
if (routeStep != null && !principal.isAnonymous()) {
|
||||
document.add("route_step", Json.createObjectBuilder()
|
||||
.add("name", routeStep.getName())
|
||||
.add("type", routeStep.getType().name())
|
||||
.add("transitionable", getTargetIdList(null).contains(routeStep.getTargetId())));
|
||||
}
|
||||
|
||||
return Response.ok().entity(document.build()).build();
|
||||
}
|
||||
|
||||
@ -423,7 +432,7 @@ public class DocumentResource extends BaseResource {
|
||||
if (documentCriteria.getTagIdList() == null) {
|
||||
documentCriteria.setTagIdList(new ArrayList<String>());
|
||||
}
|
||||
if (tagDtoList.size() == 0) {
|
||||
if (tagDtoList.isEmpty()) {
|
||||
// No tag found, the request must returns nothing
|
||||
documentCriteria.getTagIdList().add(UUID.randomUUID().toString());
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ public class RouteModelResource extends BaseResource {
|
||||
|
||||
try (JsonReader reader = Json.createReader(new StringReader(steps))) {
|
||||
JsonArray stepsJson = reader.readArray();
|
||||
if (stepsJson.size() == 0) {
|
||||
if (stepsJson.isEmpty()) {
|
||||
throw new ClientException("ValidationError", "At least one step is required");
|
||||
}
|
||||
for (int i = 0; i < stepsJson.size(); i++) {
|
||||
|
@ -3,8 +3,14 @@ package com.sismics.docs.rest.resource;
|
||||
import com.sismics.docs.core.constant.AclTargetType;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
import com.sismics.docs.core.constant.RouteStepType;
|
||||
import com.sismics.docs.core.dao.jpa.*;
|
||||
import com.sismics.docs.core.model.jpa.*;
|
||||
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||
import com.sismics.docs.core.dao.jpa.RouteDao;
|
||||
import com.sismics.docs.core.dao.jpa.RouteModelDao;
|
||||
import com.sismics.docs.core.dao.jpa.RouteStepDao;
|
||||
import com.sismics.docs.core.model.jpa.Route;
|
||||
import com.sismics.docs.core.model.jpa.RouteModel;
|
||||
import com.sismics.docs.core.model.jpa.RouteStep;
|
||||
import com.sismics.docs.core.util.SecurityUtil;
|
||||
import com.sismics.rest.exception.ClientException;
|
||||
import com.sismics.rest.exception.ForbiddenClientException;
|
||||
|
||||
@ -69,8 +75,6 @@ public class RouteResource extends BaseResource {
|
||||
routeDao.create(route, principal.getId());
|
||||
|
||||
// Create the steps
|
||||
UserDao userDao = new UserDao();
|
||||
GroupDao groupDao = new GroupDao();
|
||||
RouteStepDao routeStepDao = new RouteStepDao();
|
||||
try (JsonReader reader = Json.createReader(new StringReader(routeModel.getSteps()))) {
|
||||
JsonArray stepsJson = reader.readArray();
|
||||
@ -85,22 +89,8 @@ public class RouteResource extends BaseResource {
|
||||
.setRouteId(route.getId())
|
||||
.setName(step.getString("name"))
|
||||
.setOrder(order++)
|
||||
.setType(RouteStepType.valueOf(step.getString("type")));
|
||||
|
||||
switch (targetType) {
|
||||
case USER:
|
||||
User user = userDao.getActiveByUsername(targetName);
|
||||
if (user != null) {
|
||||
routeStep.setTargetId(user.getId());
|
||||
}
|
||||
break;
|
||||
case GROUP:
|
||||
Group group = groupDao.getActiveByName(targetName);
|
||||
if (group != null) {
|
||||
routeStep.setTargetId(group.getId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
.setType(RouteStepType.valueOf(step.getString("type")))
|
||||
.setTargetId(SecurityUtil.getTargetIdFromName(targetName, targetType));
|
||||
|
||||
if (routeStep.getTargetId() == null) {
|
||||
throw new ClientException("InvalidRouteModel", "A step has an invalid target");
|
||||
|
@ -116,7 +116,7 @@ public class ShareResource extends BaseResource {
|
||||
// Check that the user can share the linked document
|
||||
AclDao aclDao = new AclDao();
|
||||
List<Acl> aclList = aclDao.getByTargetId(id);
|
||||
if (aclList.size() == 0) {
|
||||
if (aclList.isEmpty()) {
|
||||
throw new ClientException("ShareNotFound", MessageFormat.format("Share not found: {0}", id));
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ public class TagResource extends BaseResource {
|
||||
|
||||
TagDao tagDao = new TagDao();
|
||||
List<TagDto> tagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)).setId(id), null);
|
||||
if (tagDtoList.size() == 0) {
|
||||
if (tagDtoList.isEmpty()) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ public class TestDocumentResource extends BaseJerseyTest {
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, document3Token)
|
||||
.get(JsonObject.class);
|
||||
documents = json.getJsonArray("documents");
|
||||
Assert.assertTrue(documents.size() == 0);
|
||||
Assert.assertTrue(documents.isEmpty());
|
||||
|
||||
// Create a document with document3
|
||||
long create3Date = new Date().getTime();
|
||||
|
@ -22,6 +22,10 @@ public class TestRouteResource extends BaseJerseyTest {
|
||||
*/
|
||||
@Test
|
||||
public void testRouteResource() {
|
||||
// Login route1
|
||||
clientUtil.createUser("route1");
|
||||
String route1Token = clientUtil.login("route1");
|
||||
|
||||
// Login admin
|
||||
String adminToken = clientUtil.login("admin", "admin", false);
|
||||
|
||||
@ -30,7 +34,7 @@ public class TestRouteResource extends BaseJerseyTest {
|
||||
.queryParam("sort_column", "2")
|
||||
.queryParam("asc", "false")
|
||||
.request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||
.get(JsonObject.class);
|
||||
JsonArray routeModels = json.getJsonArray("routemodels");
|
||||
Assert.assertEquals(1, routeModels.size());
|
||||
@ -38,7 +42,7 @@ public class TestRouteResource extends BaseJerseyTest {
|
||||
// Create a document
|
||||
long create1Date = new Date().getTime();
|
||||
json = target().path("/document").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||
.put(Entity.form(new Form()
|
||||
.param("title", "My super title document 1")
|
||||
.param("description", "My super description for document 1")
|
||||
@ -46,11 +50,37 @@ public class TestRouteResource extends BaseJerseyTest {
|
||||
.param("create_date", Long.toString(create1Date))), JsonObject.class);
|
||||
String document1Id = json.getString("id");
|
||||
|
||||
// Start the default route on document1
|
||||
// Start the default route on document 1
|
||||
target().path("/route/start").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||
.post(Entity.form(new Form()
|
||||
.param("documentId", document1Id)
|
||||
.param("routeModelId", routeModels.getJsonObject(0).getString("id"))), JsonObject.class);
|
||||
|
||||
// Add an ACL READ for admin with route1
|
||||
// TODO Remove me when ACLs are automatically added on route step targets
|
||||
target().path("/acl").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||
.put(Entity.form(new Form()
|
||||
.param("source", document1Id)
|
||||
.param("perm", "READ")
|
||||
.param("target", "admin")
|
||||
.param("type", "USER")), JsonObject.class);
|
||||
|
||||
// Get document 1 as route1
|
||||
json = target().path("/document/" + document1Id).request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||
.get(JsonObject.class);
|
||||
JsonObject routeStep = json.getJsonObject("route_step");
|
||||
Assert.assertNotNull(routeStep);
|
||||
Assert.assertFalse(routeStep.getBoolean("transitionable"));
|
||||
|
||||
// Get document 1 as admin
|
||||
json = target().path("/document/" + document1Id).request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.get(JsonObject.class);
|
||||
routeStep = json.getJsonObject("route_step");
|
||||
Assert.assertNotNull(routeStep);
|
||||
Assert.assertTrue(routeStep.getBoolean("transitionable"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user