View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.framework.support.krms;
17  
18  import org.junit.Test;
19  import org.kuali.rice.kew.api.KewApiServiceLocator;
20  import org.kuali.rice.kew.api.WorkflowDocument;
21  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
22  import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
23  import org.kuali.rice.kew.test.KEWTestCase;
24  import org.kuali.rice.krad.service.BusinessObjectService;
25  import org.kuali.rice.krad.service.KRADServiceLocator;
26  import org.kuali.rice.krms.api.KrmsApiServiceLocator;
27  import org.kuali.rice.krms.api.KrmsConstants;
28  import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
29  import org.kuali.rice.krms.api.repository.type.KrmsTypeAttribute;
30  import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
31  import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
32  import org.kuali.rice.krms.impl.repository.ActionAttributeBo;
33  import org.kuali.rice.krms.impl.repository.ActionBo;
34  import org.kuali.rice.krms.impl.repository.AgendaAttributeBo;
35  import org.kuali.rice.krms.impl.repository.AgendaBo;
36  import org.kuali.rice.krms.impl.repository.AgendaItemBo;
37  import org.kuali.rice.krms.impl.repository.ContextBo;
38  import org.kuali.rice.krms.impl.repository.KrmsAttributeDefinitionBo;
39  import org.kuali.rice.krms.impl.repository.KrmsAttributeDefinitionService;
40  import org.kuali.rice.krms.impl.repository.RuleBo;
41  import org.kuali.rice.krms.impl.util.KRMSServiceLocatorInternal;
42  import org.kuali.rice.test.BaselineTestCase;
43  
44  import java.util.ArrayList;
45  import java.util.HashSet;
46  import java.util.List;
47  import java.util.Set;
48  
49  import static org.junit.Assert.*;
50  
51  /**
52   * An integration test which tests KEW integration with KRMS producing PeopleFlows for routing purposes.  KEW provides
53   * standard integration with KRMS through the use of it's {@code <rulesEngine executorClass="..."/>} element, which is
54   * what this test is testing.
55   *
56   * @author Kuali Rice Team (rice.collab@kuali.org)
57   */
58  @BaselineTestCase.BaselineMode(BaselineTestCase.Mode.CLEAR_DB)
59  public class KewToRulesEngineIntegrationTest extends KEWTestCase {
60  
61      private static final String SIMPLE_DOCUMENT_TYPE = "RulesEngineIntegration-Simple";
62  
63      private static final String PEOPLE_FLOW_ID_ATTRIBUTE = "peopleFlowId";
64      private static final String EVENT_ATTRIBUTE = "Event";
65  
66      private BusinessObjectService businessObjectService;
67  
68      private KrmsAttributeDefinition peopleFlowIdAttributeDefinition;
69      private KrmsTypeDefinition approvalPeopleFlowActionType;
70      private RuleBo ruleBo;
71  
72  
73      @Override
74      protected void loadTestData() throws Exception {
75          loadXmlFile("KewToRulesEngineIntegrationTest.xml");
76          businessObjectService = KRADServiceLocator.getBusinessObjectService();
77          assertNotNull(businessObjectService);
78          PeopleFlowDefinition peopleFlow = createFirstPeopleFlow();
79          this.peopleFlowIdAttributeDefinition = createPeopleFlowIdAttributeDefinition();
80          KrmsAttributeDefinitionBo eventAttributeDefinition = createEventAttributeDefinition();
81          this.approvalPeopleFlowActionType = createApprovalPeopleFlowActionType(peopleFlowIdAttributeDefinition);
82          this.ruleBo = createRule(approvalPeopleFlowActionType, peopleFlowIdAttributeDefinition, peopleFlow.getId());
83          ContextBo contextBo = createContext();
84          createAgenda(ruleBo, contextBo, eventAttributeDefinition);
85      }
86  
87      private PeopleFlowDefinition createFirstPeopleFlow() {
88          String user1 = getPrincipalIdForName("user1");
89          String user2 = getPrincipalIdForName("user2");
90          String testWorkgroup = getGroupIdForName("KR-WKFLW", "TestWorkgroup");
91          PeopleFlowDefinition.Builder peopleFlow = PeopleFlowDefinition.Builder.create("TEST", "PeopleFlow1");
92          peopleFlow.addPrincipal(user1).setPriority(1);
93          peopleFlow.addPrincipal(user2).setPriority(2);
94          peopleFlow.addGroup(testWorkgroup).setPriority(3);
95          return KewApiServiceLocator.getPeopleFlowService().createPeopleFlow(peopleFlow.build());
96      }
97  
98      /**
99       * Create an attribute definition for "peopleFlowId" which can be used on PeopleFlow-related action types.
100      */
101     private KrmsAttributeDefinition createPeopleFlowIdAttributeDefinition() {
102         KrmsAttributeDefinitionService service = KRMSServiceLocatorInternal.getService("krmsAttributeDefinitionService");
103         assertNotNull(service);
104         KrmsAttributeDefinitionBo attributeDefinitionBo = new KrmsAttributeDefinitionBo();
105         attributeDefinitionBo.setNamespace(KrmsConstants.KRMS_NAMESPACE);
106         attributeDefinitionBo.setName(PEOPLE_FLOW_ID_ATTRIBUTE);
107         attributeDefinitionBo.setLabel("PeopleFlow ID");
108         attributeDefinitionBo.setActive(true);
109         attributeDefinitionBo = businessObjectService.save(attributeDefinitionBo);
110         assertNotNull(attributeDefinitionBo.getId());
111         return KrmsAttributeDefinitionBo.to(attributeDefinitionBo);
112     }
113 
114     /**
115      * Creates the KRMS Type for PeopleFlow approval actions.
116      */
117     private KrmsTypeDefinition createApprovalPeopleFlowActionType(KrmsAttributeDefinition peopleFlowIdAttributeDefinition) {
118         KrmsTypeRepositoryService krmsTypeRepositoryService = KrmsApiServiceLocator.getKrmsTypeRepositoryService();
119         KrmsTypeDefinition.Builder typeDefinition = KrmsTypeDefinition.Builder.create(KrmsConstants.KRMS_NAMESPACE, "approvalPeopleFlowActionType");
120         typeDefinition.setServiceName("approvalPeopleFlowActionTypeService");
121         KrmsTypeAttribute.Builder attributeDefinition = KrmsTypeAttribute.Builder.create(null, peopleFlowIdAttributeDefinition.getId(), 1);
122         typeDefinition.getAttributes().add(attributeDefinition);
123         KrmsTypeDefinition approvalPeopleFlowActionType = krmsTypeRepositoryService.createKrmsType(typeDefinition.build());
124         assertNotNull(approvalPeopleFlowActionType);
125         assertNotNull(approvalPeopleFlowActionType.getId());
126         assertEquals(1, approvalPeopleFlowActionType.getAttributes().size());
127         assertNotNull(approvalPeopleFlowActionType.getAttributes().get(0).getId());
128         assertEquals(approvalPeopleFlowActionType.getId(), approvalPeopleFlowActionType.getAttributes().get(0).getTypeId());
129         return approvalPeopleFlowActionType;
130     }
131 
132     /**
133      * Creates a rule linked with the given action type and people flow action
134      * @param actionType
135      * @param peopleFlowIdAttributeDefinition
136      * @param peopleFlowId
137      */
138     private RuleBo createRule(KrmsTypeDefinition actionType, KrmsAttributeDefinition peopleFlowIdAttributeDefinition, String peopleFlowId) {
139         RuleBo rule = new RuleBo();
140         rule.setNamespace("TEST");
141         rule.setName("PeopleFlowRule");
142         // no propositions on this rule so it should (hopefully) always evaluate to true
143 
144         List<ActionBo> actions = new ArrayList<ActionBo>();
145         rule.setActions(actions);
146 
147         // create the action with an attribute pointing to a peopleflow
148         ActionBo peopleFlowAction = new ActionBo();
149         actions.add(peopleFlowAction);
150         peopleFlowAction.setNamespace("TEST");
151         peopleFlowAction.setName("PeopleFlowApprovalAction");
152         peopleFlowAction.setSequenceNumber(1);
153         peopleFlowAction.setTypeId(actionType.getId());
154         Set<ActionAttributeBo> actionAttributes = new HashSet<ActionAttributeBo>();
155         peopleFlowAction.setAttributeBos(actionAttributes);
156         ActionAttributeBo actionAttribute = new ActionAttributeBo();
157         actionAttributes.add(actionAttribute);
158         actionAttribute.setAttributeDefinitionId(peopleFlowIdAttributeDefinition.getId());
159         actionAttribute.setAttributeDefinition(KrmsAttributeDefinitionBo.from(peopleFlowIdAttributeDefinition));
160         actionAttribute.setValue(peopleFlowId);
161 
162         // set up a simple default type for the rule
163         KrmsTypeRepositoryService krmsTypeRepositoryService = KrmsApiServiceLocator.getKrmsTypeRepositoryService();
164         KrmsTypeDefinition.Builder typeDefinition = KrmsTypeDefinition.Builder.create("Name", KrmsConstants.KRMS_NAMESPACE);
165         typeDefinition.setServiceName("defaultRuleTypeService");
166         KrmsTypeDefinition defaultRuleType = krmsTypeRepositoryService.createKrmsType(typeDefinition.build());
167         assertNotNull(defaultRuleType);
168         assertNotNull(defaultRuleType.getId());
169 
170         // now assign the default type to the rule and save it
171         rule.setTypeId(defaultRuleType.getId());
172         rule = businessObjectService.save(rule);
173         assertNotNull(rule.getId());
174         assertEquals(1, rule.getActions().size());
175         assertNotNull(rule.getActions().get(0).getId());
176         assertEquals(1, rule.getActions().get(0).getAttributeBos().size());
177         return rule;
178     }
179 
180     private ContextBo createContext() {
181         // set up a simple default type for the context
182         KrmsTypeRepositoryService krmsTypeRepositoryService = KrmsApiServiceLocator.getKrmsTypeRepositoryService();
183         KrmsTypeDefinition.Builder typeDefinition = KrmsTypeDefinition.Builder.create(KrmsConstants.KRMS_NAMESPACE, "DefaultContextType");
184         KrmsTypeDefinition defaultContextType = krmsTypeRepositoryService.createKrmsType(typeDefinition.build());
185 
186         ContextBo contextBo = new ContextBo();
187         contextBo.setNamespace(KrmsConstants.KRMS_NAMESPACE);
188         contextBo.setName("MyContext");
189         contextBo.setTypeId(defaultContextType.getId());
190         return businessObjectService.save(contextBo);
191     }
192 
193     /**
194      * Create an attribute definition for "Event" which is used to define the triggering event on an agenda.
195      */
196     private KrmsAttributeDefinitionBo createEventAttributeDefinition() {
197         KrmsAttributeDefinitionService service = KRMSServiceLocatorInternal.getService("krmsAttributeDefinitionService");
198         assertNotNull(service);
199         KrmsAttributeDefinitionBo attributeDefinitionBo = new KrmsAttributeDefinitionBo();
200         attributeDefinitionBo.setNamespace(KrmsConstants.KRMS_NAMESPACE);
201         attributeDefinitionBo.setName(EVENT_ATTRIBUTE);
202         attributeDefinitionBo.setLabel("Event");
203         attributeDefinitionBo.setActive(true);
204         attributeDefinitionBo = businessObjectService.save(attributeDefinitionBo);
205         assertNotNull(attributeDefinitionBo.getId());
206         return attributeDefinitionBo;
207     }
208 
209     private AgendaBo createAgenda(RuleBo ruleBo, ContextBo contextBo, KrmsAttributeDefinitionBo eventAttributeDefinition) {
210         // set up a simple default type for the agenda
211         KrmsTypeRepositoryService krmsTypeRepositoryService = KrmsApiServiceLocator.getKrmsTypeRepositoryService();
212         KrmsTypeDefinition.Builder typeDefinition = KrmsTypeDefinition.Builder.create(KrmsConstants.KRMS_NAMESPACE, "DefaultAgendaType");
213         KrmsTypeDefinition defaultAgendaType = krmsTypeRepositoryService.createKrmsType(typeDefinition.build());
214 
215         AgendaBo agendaBo = new AgendaBo();
216         agendaBo.setActive(true);
217         agendaBo.setContextId(contextBo.getId());
218         agendaBo.setName("MyAgenda");
219         agendaBo.setTypeId(defaultAgendaType.getId());
220         agendaBo = businessObjectService.save(agendaBo);
221 
222         agendaBo.setFirstItemId(ruleBo.getId());
223         AgendaItemBo agendaItemBo = new AgendaItemBo();
224         agendaItemBo.setRule(ruleBo);
225         agendaItemBo.setAgendaId(agendaBo.getId());
226         agendaItemBo = businessObjectService.save(agendaItemBo);
227 
228         List<AgendaItemBo> agendaItems = new ArrayList<AgendaItemBo>();
229         agendaItems.add(agendaItemBo);
230         agendaBo.setItems(agendaItems);
231         agendaBo.setFirstItemId(agendaItemBo.getId());
232 
233         // also add attribute to the agenda to store event
234         Set<AgendaAttributeBo> agendaAttributes = new HashSet<AgendaAttributeBo>();
235         agendaBo.setAttributeBos(agendaAttributes);
236         AgendaAttributeBo agendaAttribute = new AgendaAttributeBo();
237         agendaAttributes.add(agendaAttribute);
238         agendaAttribute.setAttributeDefinitionId(eventAttributeDefinition.getId());
239         agendaAttribute.setAttributeDefinition(eventAttributeDefinition);
240         agendaAttribute.setValue("workflow");
241         agendaBo = businessObjectService.save(agendaBo);
242 
243         contextBo.getAgendas().add(agendaBo);
244 
245         return agendaBo;
246     }
247 
248     @Test
249     public void testSimpleKrmsPeopleFlowRules() throws Exception {
250         WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("user3"), SIMPLE_DOCUMENT_TYPE);
251         document.route("");
252         assertTrue(document.isEnroute());
253 
254         String user1 = getPrincipalIdForName("user1");
255         String user2 = getPrincipalIdForName("user2");
256         String ewestfal = getPrincipalIdForName("ewestfal"); // ewestfal is a member of TestWorkgroup
257         // at this point, the PeopleFlow should have triggered requests to user1, user2, and TestWorkgroup, in that order
258         // but only the request to user1 should be activated
259         document.switchPrincipal(ewestfal);
260         assertFalse(document.isApprovalRequested());
261         document.switchPrincipal(user2);
262         assertFalse(document.isApprovalRequested());
263         document.switchPrincipal(user1);
264         assertTrue(document.isApprovalRequested());
265 
266         // now approve as user1
267         document.approve("");
268         assertTrue(document.isEnroute());
269 
270         // should now be activated to user2
271         document.switchPrincipal(user2);
272         assertTrue(document.isApprovalRequested());
273         document.approve("");
274         assertTrue(document.isEnroute());
275 
276         // should now be activated to TestWorkgroup, of which ewestfal is a member
277         document.switchPrincipal(ewestfal);
278         assertTrue(document.isApprovalRequested());
279         document.approve("");
280 
281         // all approvals have been taken, document should now be final
282         assertTrue(document.isFinal());
283     }
284 
285     @Test
286     public void testMultipleKrmsPeopleFlowRules() throws Exception {
287         // first, let's add a second peopleflow to the rule action setup
288         addAnotherPeopleFlow(ruleBo);
289 
290         WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("user3"), SIMPLE_DOCUMENT_TYPE);
291         document.route("");
292         assertTrue(document.isEnroute());
293 
294         String user1 = getPrincipalIdForName("user1");
295         String user2 = getPrincipalIdForName("user2");
296         String ewestfal = getPrincipalIdForName("ewestfal"); // ewestfal is a member of TestWorkgroup
297         // at this point, the PeopleFlow should have triggered requests to user1, user2, and TestWorkgroup, in that order
298         // but only the request to user1 should be activated
299         document.switchPrincipal(ewestfal);
300         assertFalse(document.isApprovalRequested());
301         document.switchPrincipal(user2);
302         assertFalse(document.isApprovalRequested());
303         document.switchPrincipal(user1);
304         assertTrue(document.isApprovalRequested());
305         // there should also only be 3 action requests, action request to second peopleflow should not yet be generated
306         assertEquals(3, document.getRootActionRequests().size());
307 
308         // now approve as user1
309         document.approve("");
310         assertTrue(document.isEnroute());
311 
312         // should now be activated to user2
313         document.switchPrincipal(user2);
314         assertTrue(document.isApprovalRequested());
315         document.approve("");
316         assertTrue(document.isEnroute());
317 
318         // should now be activated to TestWorkgroup, of which ewestfal is a member
319         document.switchPrincipal(ewestfal);
320         assertTrue(document.isApprovalRequested());
321         document.approve("");
322 
323         // document should still be enroute, and now we should be routed to second peopleflow
324         assertTrue(document.isEnroute());
325         String testuser1 = getPrincipalIdForName("testuser1");
326         document.switchPrincipal(testuser1);
327         assertTrue(document.isApprovalRequested());
328         // there should be 4 action requests total now
329         assertEquals(4, document.getRootActionRequests().size());
330         document.approve("");
331 
332         // all approvals have been taken, document should now be final
333         assertTrue(document.isFinal());
334     }
335 
336     private void addAnotherPeopleFlow(RuleBo ruleBo) {
337         String testuser1 = getPrincipalIdForName("testuser1");
338         PeopleFlowDefinition.Builder peopleFlowBuilder = PeopleFlowDefinition.Builder.create("TEST", "PeopleFlow2");
339         peopleFlowBuilder.addPrincipal(testuser1).setPriority(1);
340         PeopleFlowDefinition peopleFlow =  KewApiServiceLocator.getPeopleFlowService().createPeopleFlow(peopleFlowBuilder.build());
341 
342         // create the action with an attribute pointing to a peopleflow
343         ActionBo peopleFlowAction = new ActionBo();
344         ruleBo.getActions().add(peopleFlowAction);
345         peopleFlowAction.setNamespace("TEST");
346         peopleFlowAction.setName("PeopleFlowApprovalAction2");
347         peopleFlowAction.setSequenceNumber(2);
348         peopleFlowAction.setTypeId(approvalPeopleFlowActionType.getId());
349         Set<ActionAttributeBo> actionAttributes = new HashSet<ActionAttributeBo>();
350         peopleFlowAction.setAttributeBos(actionAttributes);
351         ActionAttributeBo actionAttribute = new ActionAttributeBo();
352         actionAttributes.add(actionAttribute);
353         actionAttribute.setAttributeDefinitionId(peopleFlowIdAttributeDefinition.getId());
354         actionAttribute.setAttributeDefinition(KrmsAttributeDefinitionBo.from(peopleFlowIdAttributeDefinition));
355         actionAttribute.setValue(peopleFlow.getId());
356 
357         businessObjectService.save(ruleBo);
358         
359     }
360 
361 }
362