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