#202 Actions on route transitions (#204)

#202 Actions on route transition
This commit is contained in:
Benjamin Gamard
2018-03-12 22:55:04 +01:00
committed by GitHub
parent 9ca27d7c12
commit 995e45d28f
19 changed files with 472 additions and 50 deletions
@@ -41,7 +41,7 @@ public class AuditLogResource extends BaseResource {
* @apiSuccess {String} logs.id ID
* @apiSuccess {String} logs.username Username
* @apiSuccess {String} logs.target Entity ID
* @apiSuccess {String="Acl","Comment","Document","File","Group","Tag","User","RouteModel"} logs.class Entity type
* @apiSuccess {String="Acl","Comment","Document","File","Group","Tag","User","RouteModel","Route"} logs.class Entity type
* @apiSuccess {String="CREATE","UPDATE","DELETE"} logs.type Type
* @apiSuccess {String} logs.message Message
* @apiSuccess {Number} logs.create_date Create date (timestamp)
@@ -1,12 +1,18 @@
package com.sismics.docs.rest.resource;
import com.google.common.collect.Lists;
import com.sismics.docs.core.constant.AclTargetType;
import com.sismics.docs.core.constant.ActionType;
import com.sismics.docs.core.constant.RouteStepTransition;
import com.sismics.docs.core.constant.RouteStepType;
import com.sismics.docs.core.dao.jpa.GroupDao;
import com.sismics.docs.core.dao.jpa.RouteModelDao;
import com.sismics.docs.core.dao.jpa.TagDao;
import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.dao.jpa.criteria.RouteModelCriteria;
import com.sismics.docs.core.dao.jpa.criteria.TagCriteria;
import com.sismics.docs.core.dao.jpa.dto.RouteModelDto;
import com.sismics.docs.core.dao.jpa.dto.TagDto;
import com.sismics.docs.core.model.jpa.Group;
import com.sismics.docs.core.model.jpa.RouteModel;
import com.sismics.docs.core.model.jpa.User;
@@ -120,6 +126,7 @@ public class RouteModelResource extends BaseResource {
private void validateRouteModelSteps(String steps) {
UserDao userDao = new UserDao();
GroupDao groupDao = new GroupDao();
TagDao tagDao = new TagDao();
try (JsonReader reader = Json.createReader(new StringReader(steps))) {
JsonArray stepsJson = reader.readArray();
@@ -128,19 +135,26 @@ public class RouteModelResource extends BaseResource {
}
for (int i = 0; i < stepsJson.size(); i++) {
JsonObject step = stepsJson.getJsonObject(i);
if (step.size() != 3) {
if (step.size() != 4) {
throw new ClientException("ValidationError", "Steps data not valid");
}
String type = step.getString("type");
// Name
ValidationUtil.validateLength(step.getString("name"), "step.name", 1, 200, false);
// Type
String typeStr = step.getString("type");
RouteStepType type;
try {
RouteStepType.valueOf(type);
type = RouteStepType.valueOf(typeStr);
} catch (IllegalArgumentException e) {
throw new ClientException("ValidationError", type + "is not a valid route step type");
throw new ClientException("ValidationError", typeStr + "is not a valid route step type");
}
// Target
JsonObject target = step.getJsonObject("target");
if (target.size() != 2) {
throw new ClientException("ValidationError", "Steps data not valid");
throw new ClientException("ValidationError", "Step target is not valid");
}
AclTargetType targetType;
String targetTypeStr = target.getString("type");
@@ -166,6 +180,69 @@ public class RouteModelResource extends BaseResource {
}
break;
}
// Transitions
List<RouteStepTransition> transitionsNames = Lists.newArrayList();
JsonArray transitions = step.getJsonArray("transitions");
if (type == RouteStepType.VALIDATE) {
if (transitions.size() != 1) {
throw new ClientException("ValidationError", "VALIDATE steps should have one transition");
}
transitionsNames.add(RouteStepTransition.VALIDATED);
} else if (type == RouteStepType.APPROVE) {
if (transitions.size() != 2) {
throw new ClientException("ValidationError", "APPROVE steps should have two transition");
}
transitionsNames.add(RouteStepTransition.APPROVED);
transitionsNames.add(RouteStepTransition.REJECTED);
}
for (int j = 0; j < transitions.size(); j++) {
// Transition
JsonObject transition = transitions.getJsonObject(j);
if (transition.size() != 2) {
throw new ClientException("ValidationError", "Transition data is not valid");
}
// Transition name
String routeStepTransitionStr = transition.getString("name");
ValidationUtil.validateRequired(routeStepTransitionStr, "step.transitions.name");
RouteStepTransition routeStepTransition;
try {
routeStepTransition = RouteStepTransition.valueOf(routeStepTransitionStr);
} catch (IllegalArgumentException e) {
throw new ClientException("ValidationError", routeStepTransitionStr + " is not a valid route step transition type");
}
if (!transitionsNames.contains(routeStepTransition)) {
throw new ClientException("ValidationError", routeStepTransitionStr + " is not allowed for this step type");
}
// Actions
JsonArray actions = transition.getJsonArray("actions");
for (int k = 0; k < actions.size(); k++) {
JsonObject action = actions.getJsonObject(k);
// Action type
String actionTypeStr = action.getString("type");
ActionType actionType;
ValidationUtil.validateRequired(routeStepTransitionStr, "step.transitions.actions.type");
try {
actionType = ActionType.valueOf(actionTypeStr);
} catch (IllegalArgumentException e) {
throw new ClientException("ValidationError", actionTypeStr + " is not a valid action type");
}
// Action custom fields
if (actionType == ActionType.ADD_TAG) {
String tagId = action.getString("tag");
ValidationUtil.validateRequired(routeStepTransitionStr, "step.transitions.actions.tag");
List<TagDto> tagDtoList = tagDao.findByCriteria(new TagCriteria().setId(tagId), null);
if (tagDtoList.size() != 1) {
throw new ClientException("ValidationError", tagId + " is not a valid tag");
}
}
}
}
}
} catch (JsonException e) {
throw new ClientException("ValidationError", "Steps data not valid");
@@ -1,9 +1,6 @@
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.RouteStepTransition;
import com.sismics.docs.core.constant.RouteStepType;
import com.sismics.docs.core.constant.*;
import com.sismics.docs.core.dao.jpa.*;
import com.sismics.docs.core.dao.jpa.criteria.RouteCriteria;
import com.sismics.docs.core.dao.jpa.criteria.RouteStepCriteria;
@@ -13,6 +10,7 @@ import com.sismics.docs.core.dao.jpa.dto.RouteStepDto;
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.ActionUtil;
import com.sismics.docs.core.util.RoutingUtil;
import com.sismics.docs.core.util.SecurityUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
@@ -93,12 +91,17 @@ public class RouteResource extends BaseResource {
JsonObject target = step.getJsonObject("target");
AclTargetType targetType = AclTargetType.valueOf(target.getString("type"));
String targetName = target.getString("name");
String transitions = null;
if (step.containsKey("transitions")) {
transitions = step.getJsonArray("transitions").toString();
}
RouteStep routeStep = new RouteStep()
.setRouteId(route.getId())
.setName(step.getString("name"))
.setOrder(order)
.setType(RouteStepType.valueOf(step.getString("type")))
.setTransitions(transitions)
.setTargetId(SecurityUtil.getTargetIdFromName(targetName, targetType));
if (routeStep.getTargetId() == null) {
@@ -149,7 +152,9 @@ public class RouteResource extends BaseResource {
// Get the document
AclDao aclDao = new AclDao();
if (!aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(null))) {
DocumentDao documentDao = new DocumentDao();
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(null));
if (documentDto == null) {
throw new NotFoundException();
}
@@ -168,18 +173,39 @@ public class RouteResource extends BaseResource {
// Validate data
ValidationUtil.validateRequired(transitionStr, "transition");
comment = ValidationUtil.validateLength(comment, "comment", 1, 500, true);
RouteStepTransition transition = RouteStepTransition.valueOf(transitionStr);
if (routeStepDto.getType() == RouteStepType.VALIDATE && transition != RouteStepTransition.VALIDATED
|| routeStepDto.getType() == RouteStepType.APPROVE && transition != RouteStepTransition.APPROVED && transition != RouteStepTransition.REJECTED) {
RouteStepTransition routeStepTransition = RouteStepTransition.valueOf(transitionStr);
if (routeStepDto.getType() == RouteStepType.VALIDATE && routeStepTransition != RouteStepTransition.VALIDATED
|| routeStepDto.getType() == RouteStepType.APPROVE
&& routeStepTransition != RouteStepTransition.APPROVED && routeStepTransition != RouteStepTransition.REJECTED) {
throw new ClientException("ValidationError", "Invalid transition for this route step type");
}
// Validate the step and update ACLs
routeStepDao.endRouteStep(routeStepDto.getId(), transition, comment, principal.getId());
routeStepDao.endRouteStep(routeStepDto.getId(), routeStepTransition, comment, principal.getId());
RouteStepDto newRouteStep = routeStepDao.getCurrentStep(documentId);
RoutingUtil.updateAcl(documentId, newRouteStep, routeStepDto, principal.getId());
RoutingUtil.sendRouteStepEmail(documentId, routeStepDto);
// Execute actions
if (routeStepDto.getTransitions() != null) {
try (JsonReader reader = Json.createReader(new StringReader(routeStepDto.getTransitions()))) {
JsonArray transitions = reader.readArray();
// Filter out our transition
for (int i = 0; i < transitions.size(); i++) {
JsonObject transition = transitions.getJsonObject(i);
if (transition.getString("name").equals(routeStepTransition.name())) {
// Transition found, execute those actions
JsonArray actions = transition.getJsonArray("actions");
for (int j = 0; j < actions.size(); j++) {
JsonObject action = actions.getJsonObject(j);
ActionType actionType = ActionType.valueOf(action.getString("type"));
ActionUtil.executeAction(actionType, action, documentDto);
}
}
}
}
}
JsonObjectBuilder response = Json.createObjectBuilder()
.add("readable", aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(null)));
if (newRouteStep != null) {