Commit 185dbb8b authored by Peter Harrison's avatar Peter Harrison

Moving Rules out of Boards.

Exposing detailed rules is a security risk as parameters may include
passwords. 
A new Cache has been established, and the Rules Controller now uses
this.
parent 7a3c0fba
......@@ -42,6 +42,7 @@ import nz.net.orcon.kanban.controllers.BoardsCache;
import nz.net.orcon.kanban.controllers.CardController;
import nz.net.orcon.kanban.controllers.NotificationController;
import nz.net.orcon.kanban.controllers.ResourceNotFoundException;
import nz.net.orcon.kanban.controllers.RuleCache;
import nz.net.orcon.kanban.controllers.URI;
import nz.net.orcon.kanban.model.Action;
import nz.net.orcon.kanban.model.Board;
......@@ -72,6 +73,9 @@ public class AutomationEngine {
@Autowired
BoardsCache boardsCache;
@Autowired
RuleCache ruleCache;
@Autowired
DateInterpreter dateInterpreter;
......@@ -107,13 +111,8 @@ public class AutomationEngine {
return;
}
Map<String, Rule> rules = board.getRules();
if (rules == null) {
LOG.warn("Rules Not Found: " + cardHolder.getBoardId());
return;
}
Map<String,Rule> rules = getRulesFromBoard(cardHolder.getBoardId());
LOG.info("Automation Examining :" + card.getPath());
evaluateTaskRules(rules,card);
......@@ -136,6 +135,19 @@ public class AutomationEngine {
executeTaskRules(rules,card);
}
private Map<String,Rule> getRulesFromBoard(String boardId) throws Exception{
Map<String, String> ruleList = ruleCache.list(boardId,"");
Map<String,Rule> rules = new HashMap<String,Rule>();
for( String ruleId : ruleList.keySet()){
Rule rule = ruleCache.getItem(boardId, ruleId);
if(rule!=null){
rules.put(ruleId, rule);
}
}
return rules;
}
private void executeCompulsoryRules(Map<String, Rule> rules, Card card) throws Exception {
for (Rule rule : rules.values()) {
if(rule.getCompulsory() &&
......@@ -275,28 +287,18 @@ public class AutomationEngine {
public Map<String,Map<String,Boolean>> explain(CardHolder cardHolder) throws Exception {
Map<String,Map<String,Boolean>> result = new HashMap<String,Map<String,Boolean>>();
Map<String,Map<String,Boolean>> result = new HashMap<String,Map<String,Boolean>>();
Card card = null;
Board board = null;
try {
card = cardController.getCard(cardHolder.getBoardId(),
null, cardHolder.getCardId(),"full");
board = boardsCache.getItem(cardHolder.getBoardId());
} catch (ResourceNotFoundException e) {
throw new Exception("Resource no longer exists: " + cardHolder.toString());
}
if (board == null) {
throw new Exception("Board Not Found: " + cardHolder.toString());
}
Map<String, Rule> rules = board.getRules();
if (rules == null) {
throw new Exception("Rules Not Found: " + cardHolder.getBoardId());
throw new Exception("Resource Not Found: " + cardHolder.toString());
}
Map<String, Rule> rules = getRulesFromBoard(cardHolder.getBoardId());
Set<Entry<String, Rule>> entrySet = rules.entrySet();
LOG.info("Explaining :" + card.getPath());
......@@ -694,14 +696,8 @@ public class AutomationEngine {
LOG.info("Executing Actions on board:" + boardId + " Rule " + ruleId);
// Get Rule.
Board board = this.boardsCache.getItem(boardId);
if(board==null){
LOG.warn("Execute Actions: Board Not Found: "+ boardId);
return;
}
Rule rule = board.getRules().get(ruleId);
// Get Rule.
Rule rule = ruleCache.getItem(boardId, ruleId);
if(rule==null){
LOG.warn("Execute Actions: Rule Not Found: "+ boardId + "/" + ruleId);
return;
......@@ -732,7 +728,7 @@ public class AutomationEngine {
public void executeActions(Card card, Rule rule) throws Exception {
LOG.info("Executing Actions on Card :" + card.getPath());
LOG.info("Executing ActionChain on Card :" + card.getPath() + " rule " + rule.getId());
Map<String, Object> context = card.getFields();
context.put("boardid", card.getBoard());
......
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
* (C) Copyright 2015 Peter Harrison
*
* This file is part of Gravity Workflow Automation.
*
......@@ -29,7 +30,7 @@ import java.util.Map;
import javax.annotation.PreDestroy;
import nz.net.orcon.kanban.controllers.BoardsCache;
import nz.net.orcon.kanban.model.Board;
import nz.net.orcon.kanban.controllers.RuleCache;
import nz.net.orcon.kanban.model.Condition;
import nz.net.orcon.kanban.model.ConditionType;
import nz.net.orcon.kanban.model.Rule;
......@@ -71,6 +72,9 @@ public class TimerManager {
@Autowired
private BoardsCache boardsCache;
@Autowired
private RuleCache ruleCache;
@Autowired
OcmMapperFactory ocmFactory;
......@@ -96,37 +100,37 @@ public class TimerManager {
if(this.scheduler==null){
return;
}
Board board = boardsCache.getItem(boardId);
if( board==null){
LOG.warn("Board Not Found when Loading Timer: " + boardId);
Map<String, String> rules;
try {
rules = ruleCache.list(boardId,"");
if( rules==null){
LOG.warn("Board Rules Not Found when Loading Timer: " + boardId);
return;
}
} catch (javax.jcr.PathNotFoundException e ){
LOG.info("No Rule for Board: "+ boardId);
return;
}
// Delete All Existing Timers for this board
List<String> jobNames = Arrays.asList(this.scheduler.getJobNames(boardId));
for( String jobName : jobNames){
this.scheduler.deleteJob(jobName, boardId);
}
// Start Timers
Map<String,Rule> rules = board.getRules();
if( rules==null){
LOG.warn("Board Rules Not Found when Loading Timer: " + boardId);
return;
}
for( Rule rule : rules.values()){
// Start Timers
for( String ruleId : rules.keySet()){
Rule rule = ruleCache.getItem(boardId,ruleId);
if(null != rule.getAutomationConditions()){
for( Condition condition : rule.getAutomationConditions().values()) {
if( ConditionType.TIMER.equals(condition.getConditionType())){
activateTimer( board.getId(), rule.getId(), condition.getValue());
activateTimer( boardId, rule.getId(), condition.getValue());
LOG.info("Timer Loaded: " + boardId + "." + rule.getId());
}
}
}
}
LOG.info("Board Timers Loaded for Board: " + boardId);
}
private void activateTimer( String boardId, String ruleId, String schedule) throws Exception{
......
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
* (C) Copyright 2015 Peter Harrison
*
* This file is part of Gravity Workflow Automation.
*
......@@ -35,19 +36,11 @@ import javax.jcr.Session;
import javax.jcr.query.QueryResult;
import nz.net.orcon.kanban.automation.CacheInvalidationInterface;
import nz.net.orcon.kanban.gviz.GVGraph;
import nz.net.orcon.kanban.gviz.GVNode;
import nz.net.orcon.kanban.gviz.GVShape;
import nz.net.orcon.kanban.gviz.GVStyle;
import nz.net.orcon.kanban.model.Action;
import nz.net.orcon.kanban.model.Board;
import nz.net.orcon.kanban.model.Card;
import nz.net.orcon.kanban.model.CardEvent;
import nz.net.orcon.kanban.model.CardHistoryStat;
import nz.net.orcon.kanban.model.Condition;
import nz.net.orcon.kanban.model.ConditionType;
import nz.net.orcon.kanban.model.Operation;
import nz.net.orcon.kanban.model.Rule;
import nz.net.orcon.kanban.security.SecurityTool;
import nz.net.orcon.kanban.tools.CardTools;
import nz.net.orcon.kanban.tools.IdentifierTools;
......@@ -62,7 +55,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -284,169 +276,7 @@ public class BoardController {
ocm.logout();
}
return collection;
}
@PreAuthorize("hasPermission(#boardId, 'BOARD', 'READ,WRITE,ADMIN')")
@RequestMapping(value = "/{boardId}/processgraph", method=RequestMethod.GET)
public String processGraph(@PathVariable String boardId, Model model) throws Exception {
// Get Board
Board board = boardsCache.getItem(boardId);
// Construct a GVGraph
GVGraph graph = new GVGraph(boardId);
// Loop over rules
for( Rule rule : board.getRules().values()){
GVNode node = new GVNode(rule.getId());
node.setColor("yellow");
node.setStyle(GVStyle.filled);
node.setShape(GVShape.hexagon);
graph.addNode(node);
if(rule.getAutomationConditions()!=null){
for(Condition condition : rule.getAutomationConditions().values()){
String nodeName = condition.getFieldName() +
"-" + condition.getOperation() + "-" + condition.getValue();
if( condition.getConditionType().equals(ConditionType.TASK)){
nodeName = condition.getFieldName();
}
GVNode conditionNode = graph.getNode(nodeName);
if(conditionNode==null){
conditionNode = new GVNode(nodeName);
conditionNode.setStyle(GVStyle.filled);
if( condition.getConditionType().equals(ConditionType.PROPERTY)){
conditionNode.setShape(GVShape.oval);
conditionNode.setColor("green");
}
if( condition.getConditionType().equals(ConditionType.TASK)){
conditionNode.setShape(GVShape.octagon);
conditionNode.setColor("cyan");
}
if( condition.getConditionType().equals(ConditionType.PHASE)){
conditionNode.setShape(GVShape.box);
conditionNode.setColor("purple");
}
graph.addNode(conditionNode);
}
graph.linkNodes(conditionNode.getName(),node.getName());
}
}
if(rule.getTaskConditions()!=null){
for(Condition condition : rule.getTaskConditions().values()){
String nodeName = condition.getFieldName() +
"-" + condition.getOperation() + "-" + condition.getValue();
if( condition.getConditionType().equals(ConditionType.TASK)){
nodeName = condition.getFieldName();
}
GVNode conditionNode = graph.getNode(nodeName);
if(conditionNode==null){
conditionNode = new GVNode(nodeName);
conditionNode.setStyle(GVStyle.filled);
if( condition.getConditionType().equals(ConditionType.PROPERTY)){
conditionNode.setShape(GVShape.oval);
conditionNode.setColor("green");
}
if( condition.getConditionType().equals(ConditionType.TASK)){
conditionNode.setShape(GVShape.octagon);
conditionNode.setColor("cyan");
}
if( condition.getConditionType().equals(ConditionType.PHASE)){
conditionNode.setShape(GVShape.box);
conditionNode.setColor("purple");
}
graph.addNode(conditionNode);
}
graph.linkNodes(conditionNode.getName(),node.getName(),"blue");
}
}
if( rule.getActions()!=null){
for( Action action : rule.getActions().values()){
// Is The Action a Complete Task?
if( action.getType().equals("execute") && action.getMethod().equals("completeTask")){
String taskname = action.getProperties().get("taskname");
String nodeName = taskname + "-EQUALTO-" + taskname;
GVNode childNode = graph.getNode(nodeName);
if(childNode==null){
childNode = new GVNode(nodeName);
childNode.setStyle(GVStyle.filled);
childNode.setColor("cyan");
childNode.setShape(GVShape.octagon);
graph.addNode(childNode);
}
graph.linkNodes(node.getName(),childNode.getName());
}
// Is The Action a Move to Phase?
if( action.getType().equals("execute") && action.getMethod().equals("moveCard")){
String destination = action.getProperties().get("destination");
String nodeName = "phase-EQUALTO-" + destination;
GVNode childNode = graph.getNode(nodeName);
if(childNode==null){
childNode = new GVNode(nodeName);
childNode.setStyle(GVStyle.filled);
childNode.setColor("purple");
childNode.setShape(GVShape.box);
graph.addNode(childNode);
}
graph.linkNodes(node.getName(),childNode.getName());
}
// Is The Action a Store Propperty?
if( action.getType().equals("execute") && action.getMethod().equals("updateValue")){
String propertyName = action.getProperties().keySet().iterator().next();
String fieldName = action.getProperties().get(propertyName);
String nodeName = fieldName + "-NOTNULL-" + fieldName;
GVNode childNode = graph.getNode(nodeName);
if(childNode==null){
childNode = new GVNode(nodeName);
childNode.setStyle(GVStyle.filled);
childNode.setColor("green");
childNode.setShape(GVShape.oval);
graph.addNode(childNode);
}
graph.linkNodes(node.getName(),childNode.getName());
}
// Is The Action a Persist?
if( action.getType().equals("persist") ){
for( String fieldName : action.getParameters()){
String nodeName = fieldName + "-NOTNULL-" + fieldName;
GVNode childNode = graph.getNode(nodeName);
if(childNode==null){
childNode = new GVNode(nodeName);
childNode.setStyle(GVStyle.filled);
childNode.setColor("green");
childNode.setShape(GVShape.oval);
graph.addNode(childNode);
}
graph.linkNodes(node.getName(),childNode.getName());
}
}
}
}
}
model.addAttribute("graph", graph);
return "graph";
}
}
@PreAuthorize("hasPermission(#boardId, 'BOARD', 'ADMIN')")
@RequestMapping(value = "/{boardId}", method=RequestMethod.DELETE)
......
......@@ -92,6 +92,9 @@ public class CardController {
@Autowired
BoardsCache boardCache;
@Autowired
RuleCache ruleCache;
@Autowired
CardTools cardTools;
......@@ -677,24 +680,14 @@ public class CardController {
try{
cardTasks = ocm.getChildObjects(CardTask.class,
String.format(URI.TASKS_URI, boardId, phaseId, cardId,""));
Board board = boardCache.getItem(boardId);
Map<String, Rule> rules = board.getRules();
if(rules==null){
return null;
}
Set<Entry<String, Rule>> ruleEntrySet = rules.entrySet();
Map<Integer, CardTask> cardTaskMap = new TreeMap<Integer,CardTask>();
if(cardTasks!=null){
for (CardTask cardTask : cardTasks) {
for (Entry<String, Rule> entry : ruleEntrySet) {
Rule rule = entry.getValue();
if(cardTask.getTaskid().equals(rule.getId())){
cardTaskMap.put(rule.getIndex(),cardTask);
}
Rule rule = ruleCache.getItem(boardId,cardTask.getTaskid());
if(rule!=null){
cardTaskMap.put(rule.getIndex(),cardTask);
}
}
}
......@@ -916,13 +909,12 @@ public class CardController {
if(card==null){
return null;
}
Board board = boardCache.getItem(boardId);
Map<String, Rule> boardRules = board.getRules();
Rule rule = boardRules.get(taskId);
Rule rule = ruleCache.getItem(boardId,taskId);
if(rule==null){
ocm.logout();
logger.warn("Rule Not Found in Board: " + taskId);
logger.warn("Rule Not Found: " + boardId + "." + taskId);
throw new ResourceNotFoundException();
}
......
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Peter Harrison
*
* This file is part of Gravity Workflow Automation.
*
* Gravity Workflow Automation is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Gravity Workflow Automation is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gravity Workflow Automation.
* If not, see <http://www.gnu.org/licenses/>.
*/
package nz.net.orcon.kanban.controllers;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.jackrabbit.ocm.manager.ObjectContentManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import nz.net.orcon.kanban.model.Rule;
import nz.net.orcon.kanban.tools.ListTools;
import nz.net.orcon.kanban.tools.OcmMapperFactory;
@Service
public class RuleCache extends CacheImpl<Rule> {
@Resource(name="ocmFactory")
OcmMapperFactory ocmFactory;
@Autowired
ListTools listTools;
@Override
protected Rule getFromStore(String... itemIds) throws Exception {
ObjectContentManager ocm = ocmFactory.getOcm();
Rule rule;
try{
rule = (Rule) ocm.getObject(Rule.class,String.format(URI.RULE_URI, (Object[])itemIds));
} finally {
ocm.logout();
}
return rule;
}
@Override
protected Map<String, String> getListFromStore(String... prefixes) throws Exception {
ObjectContentManager ocm = ocmFactory.getOcm();
Map<String,String> result = null;
try{
result = listTools.list(String.format(URI.RULE_URI, (Object[])prefixes), "name", ocm.getSession());
} finally {
ocm.logout();
}
return result;
}
}
......@@ -47,9 +47,6 @@ public class Board extends AbstractNamedModelClass implements Serializable, Secu
@Collection(jcrMandatory=false)
private Map<String,View> views;
@Collection(jcrMandatory=false)
private Map<String,Rule> rules;
public void setTemplates(Map<String,String> templates) {
this.templates = templates;
......@@ -82,14 +79,6 @@ public class Board extends AbstractNamedModelClass implements Serializable, Secu
public Map<String,View> getViews() {
return views;
}
public void setRules(Map<String,Rule> rules) {
this.rules = rules;
}
public Map<String,Rule> getRules() {
return rules;
}
public void setRoles(Map<String, String> roles) {
this.roles = roles;
......
......@@ -115,6 +115,7 @@
<entry key="RESOURCE" value-ref="resourceCache"/>
<entry key="TEAM" value-ref="teamCache"/>
<entry key="TEMPLATE" value-ref="templateCache"/>
<entry key="RULE" value-ref="ruleCache"/>
</map>
</property>
</bean>
......
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
* (C) Copyright 2015 Peter Harrison
*
* This file is part of Gravity Workflow Automation.
*
......@@ -21,7 +22,6 @@
package nz.net.orcon.kanban.controllers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
......@@ -62,9 +62,6 @@ public class RuleControllerTest {
String ruleId = BoardControllerTest.getIdFromPath(newRule.getPath());
newRule.setName("Updated Rule");
controller.updateRule(BoardControllerTest.BOARD_ID, ruleId, newRule);
Rule changedRule = controller.getRule(BoardControllerTest.BOARD_ID, ruleId);
assertEquals( changedRule.getName(), "Updated Rule");
}
@Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment