View Javadoc
1   /**
2    * Copyright 2005-2015 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.docsearch.service.impl;
17  
18  import org.apache.commons.lang.RandomStringUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.joda.time.DateTime;
21  import org.junit.Before;
22  import org.junit.Test;
23  import org.kuali.rice.kew.api.document.DocumentStatus;
24  import org.kuali.rice.kew.api.document.DocumentStatusCategory;
25  import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
26  import org.kuali.rice.kew.api.document.search.RouteNodeLookupLogic;
27  import org.kuali.rice.kew.docsearch.DocumentSearchInternalUtils;
28  import org.kuali.rice.kew.useroptions.UserOptions;
29  import org.kuali.rice.kew.useroptions.UserOptionsService;
30  
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Collection;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  
38  import static org.junit.Assert.assertEquals;
39  
40  /**
41   * @author Kuali Rice Team (rice.collab@kuali.org)
42   */
43  public class DocSearchSavingTest {
44  
45      private class MockUserOptionsService implements UserOptionsService {
46  
47          private HashMap<String, String> options = new HashMap<String, String>();
48  
49          public Collection<UserOptions> findByWorkflowUser(String principalId) {
50              List<UserOptions> userOpts = new ArrayList<UserOptions>();
51              for (Map.Entry<String, String> entry : options.entrySet()) {
52                  UserOptions opt = new UserOptions();
53                  opt.setOptionId(entry.getKey());
54                  opt.setOptionVal(entry.getValue());
55                  userOpts.add(opt);
56              }
57  
58              return userOpts;
59          }
60  
61          public List<UserOptions> findByUserQualified(String principalId, String likeString) {
62              String prefix = likeString.replaceAll("%", "");
63              List<UserOptions> userOpts = new ArrayList<UserOptions>();
64              for(Map.Entry<String, String> entry : options.entrySet()) {
65                  if (entry.getKey().startsWith(prefix)) {
66                      UserOptions opt = new UserOptions();
67                      opt.setOptionId(entry.getKey());
68                      opt.setOptionVal(entry.getValue());
69                      userOpts.add(opt);
70                  }
71              }
72  
73              return userOpts;
74          }
75  
76          public void save(UserOptions userOptions) {
77              options.put(userOptions.getOptionId(), userOptions.getOptionVal());
78          }
79  
80          public void save(String principalId, Map<String, String> optionsMap) {
81              options.putAll(optionsMap);
82          }
83  
84          public void save(String principalId, String optionId, String optionValue) {
85              options.put(optionId, optionValue);
86          }
87  
88          public void deleteUserOptions(UserOptions userOptions) {
89              options.remove(userOptions.getOptionId());
90          }
91  
92          public UserOptions findByOptionId(String optionId, String principalId) {
93              if (options.containsKey(optionId)) {
94                  UserOptions opt = new UserOptions();
95                  opt.setOptionId(optionId);
96                  opt.setOptionVal(options.get(optionId));
97                  return opt;
98              } else {
99                  return null;
100             }
101         }
102 
103         public List<UserOptions> retrieveEmailPreferenceUserOptions(String emailSetting) {
104             return null;
105         }
106     }
107 
108     private class MockDocumentSearchService extends DocumentSearchServiceImpl {
109         private static final int MAX_SEARCH_ITEMS = 5;
110         private static final String LAST_SEARCH_ORDER_OPTION = "DocSearch.LastSearch.Order";
111         private static final String NAMED_SEARCH_ORDER_BASE = "DocSearch.NamedSearch.";
112         private static final String LAST_SEARCH_BASE_NAME = "DocSearch.LastSearch.Holding";
113 
114         public void saveSearch(String principalId, DocumentSearchCriteria criteria) {
115             if (StringUtils.isBlank(principalId)) {
116                 return;
117             }
118 
119             try {
120                 String savedSearchString = DocumentSearchInternalUtils.marshalDocumentSearchCriteria(criteria);
121 
122                 if (StringUtils.isNotBlank(criteria.getSaveName())) {
123                     userOptionsService.save(principalId, NAMED_SEARCH_ORDER_BASE + criteria.getSaveName(), savedSearchString);
124                 } else {
125                     // first determine the current ordering
126                     UserOptions searchOrder = userOptionsService.findByOptionId(LAST_SEARCH_ORDER_OPTION, principalId);
127                     // no previous searches, save under first id
128                     if (searchOrder == null) {
129                         userOptionsService.save(principalId, LAST_SEARCH_BASE_NAME + "0", savedSearchString);
130                         userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, LAST_SEARCH_BASE_NAME + "0");
131                     } else {
132                         String[] currentOrder = searchOrder.getOptionVal().split(",");
133                         // we have reached MAX_SEARCH_ITEMS
134                         if (currentOrder.length == MAX_SEARCH_ITEMS) {
135                             // move the last item to the front of the list, and save
136                             // over this key with the new criteria
137                             // [5,4,3,2,1] => [1,5,4,3,2]
138                             String searchName = currentOrder[currentOrder.length - 1];
139                             String[] newOrder = new String[MAX_SEARCH_ITEMS];
140                             newOrder[0] = searchName;
141                             for (int i = 0; i < currentOrder.length - 1; i++) {
142                                 newOrder[i + 1] = currentOrder[i];
143                             }
144 
145                             String newSearchOrder = rejoinWithCommas(newOrder);
146                             // save the search string under the searchName (which used to be the last name in the list)
147                             userOptionsService.save(principalId, searchName, savedSearchString);
148                             userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, newSearchOrder);
149                         } else {
150                             // saves the search to the front of the list with incremented index
151                             // [3,2,1] => [4,3,2,1]
152                             // here we need to do a push to identify the highest used number which is from the
153                             // first one in the array, and then add one to it, and push the rest back one
154                             int absMax = 0;
155                             for (String aCurrentOrder : currentOrder) {
156                                 int current = new Integer(aCurrentOrder.substring(LAST_SEARCH_BASE_NAME.length(),
157                                         aCurrentOrder.length()));
158                                 if (current > absMax) {
159                                     absMax = current;
160                                 }
161                             }
162                             String searchName = LAST_SEARCH_BASE_NAME + ++absMax;
163                             String[] newOrder = new String[currentOrder.length + 1];
164                             newOrder[0] = searchName;
165                             for (int i = 0; i < currentOrder.length; i++) {
166                                 newOrder[i + 1] = currentOrder[i];
167                             }
168 
169                             String newSearchOrder = rejoinWithCommas(newOrder);
170                             // save the search string under the searchName (which used to be the last name in the list)
171                             userOptionsService.save(principalId, searchName, savedSearchString);
172                             userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, newSearchOrder);
173                         }
174                     }
175                 }
176             } catch (Exception e) {
177                 // ignore
178             }
179 
180         }
181 
182         /**
183          * Returns a String result of the String array joined with commas
184          * @param newOrder array to join with commas
185          * @return String of the newOrder array joined with commas
186          */
187         private String rejoinWithCommas(String[] newOrder) {
188             StringBuilder newSearchOrder = new StringBuilder("");
189             for (String aNewOrder : newOrder) {
190                 if (newSearchOrder.length() != 0) {
191                     newSearchOrder.append(",");
192                 }
193                 newSearchOrder.append(aNewOrder);
194             }
195             return newSearchOrder.toString();
196         }
197     }
198 
199     private MockDocumentSearchService docSearchService;
200     private MockUserOptionsService userOptionsService;
201 
202     @Before
203     public void init() {
204         userOptionsService = new MockUserOptionsService();
205         docSearchService = new MockDocumentSearchService();
206         docSearchService.setUserOptionsService(userOptionsService);
207     }
208 
209     @Test
210     public void testConsumesExceptions() {
211         // assuming a null criteria will cause an NPE
212         docSearchService.saveSearch("princ", null);
213     }
214 
215     @Test
216     public void testUnnamedDocSearch() throws Exception {
217         // mocked...
218         String princ = "not blank";
219 
220         Collection<UserOptions> allUserOptions_before = userOptionsService.findByWorkflowUser(princ);
221 
222         assertEquals(0, allUserOptions_before.size());
223         DocumentSearchCriteria c1 = saveSearch(princ, null);
224 
225         Collection<UserOptions> allUserOptions_after = userOptionsService.findByWorkflowUser(princ);
226 
227         // saves the "last doc search criteria"
228         // and a pointer to the "last doc search criteria"
229         assertEquals(allUserOptions_before.size() + 2, allUserOptions_after.size());
230 
231         assertEquals("DocSearch.LastSearch.Holding0", userOptionsService.findByOptionId("DocSearch.LastSearch.Order",
232                 princ).getOptionVal());
233         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c1), userOptionsService.findByOptionId(
234                 "DocSearch.LastSearch.Holding0", princ).getOptionVal());
235 
236         // 2nd search
237 
238         DocumentSearchCriteria c2 = saveSearch(princ, null);
239 
240         allUserOptions_after = userOptionsService.findByWorkflowUser(princ);
241 
242         // 1 more user option
243         assertEquals(allUserOptions_before.size() + 3, allUserOptions_after.size());
244         assertEquals("DocSearch.LastSearch.Holding1,DocSearch.LastSearch.Holding0", userOptionsService.findByOptionId(
245                 "DocSearch.LastSearch.Order", princ).getOptionVal());
246         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c1), userOptionsService.findByOptionId(
247                 "DocSearch.LastSearch.Holding0", princ).getOptionVal());
248         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c2), userOptionsService.findByOptionId(
249                 "DocSearch.LastSearch.Holding1", princ).getOptionVal());
250 
251         docSearchService.clearNamedSearches(princ);
252         assertEquals(0, userOptionsService.findByWorkflowUser(princ).size());
253     }
254 
255     @Test
256     public void testNamedDocSearch() throws Exception {
257         // mocked...
258         String princ = "not blank";
259         Collection<UserOptions> allUserOptions_before = userOptionsService.findByWorkflowUser(princ);
260 
261         assertEquals(0, allUserOptions_before.size());
262 
263         DocumentSearchCriteria c1 = saveSearch(princ, "save1");
264 
265         Collection<UserOptions> allUserOptions_after = userOptionsService.findByWorkflowUser(princ);
266         assertEquals(allUserOptions_before.size() + 1, allUserOptions_after.size());
267         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c1), userOptionsService.findByOptionId(
268                 "DocSearch.NamedSearch." + c1.getSaveName(), princ).getOptionVal());
269 
270         // 2nd search
271         DocumentSearchCriteria c2 = saveSearch(princ, "save2");
272 
273         allUserOptions_after = userOptionsService.findByWorkflowUser(princ);
274         // saves a second named search
275         assertEquals(allUserOptions_before.size() + 2, allUserOptions_after.size());
276         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c2), userOptionsService.findByOptionId(
277                 "DocSearch.NamedSearch." + c2.getSaveName(), princ).getOptionVal());
278 
279         docSearchService.clearNamedSearches(princ);
280         assertEquals(0, userOptionsService.findByWorkflowUser(princ).size());
281     }
282 
283     @Test
284     public void testSavedSearchOrdering() throws Exception {
285         // searches start wrapping after this...
286         Integer MAX_SEARCH_ITEMS = 5;
287         String princ = "not blank";
288 
289         Collection<UserOptions> allUserOptions_before = userOptionsService.findByWorkflowUser(princ);
290 
291         DocumentSearchCriteria c1 = saveSearch(princ, null);
292         DocumentSearchCriteria c2 = saveSearch(princ, null);
293         DocumentSearchCriteria c3 = saveSearch(princ, null);
294         DocumentSearchCriteria c4 = saveSearch(princ, null);
295         DocumentSearchCriteria c5 = saveSearch(princ, null);
296 
297         Collection<UserOptions> allUserOptions_after = userOptionsService.findByWorkflowUser(princ);
298         assertEquals(allUserOptions_before.size() + 5 + 1, allUserOptions_after.size());
299         assertEquals("DocSearch.LastSearch.Holding4,DocSearch.LastSearch.Holding3,DocSearch.LastSearch.Holding2,"
300                 + "DocSearch.LastSearch.Holding1,DocSearch.LastSearch.Holding0", userOptionsService.findByOptionId(
301                 "DocSearch.LastSearch.Order", princ).getOptionVal());
302         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c5), userOptionsService.findByOptionId(
303                 "DocSearch.LastSearch.Holding4", princ).getOptionVal());
304         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c4), userOptionsService.findByOptionId(
305                 "DocSearch.LastSearch.Holding3", princ).getOptionVal());
306         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c3), userOptionsService.findByOptionId(
307                 "DocSearch.LastSearch.Holding2", princ).getOptionVal());
308         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c2), userOptionsService.findByOptionId(
309                 "DocSearch.LastSearch.Holding1", princ).getOptionVal());
310         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c1), userOptionsService.findByOptionId(
311                 "DocSearch.LastSearch.Holding0", princ).getOptionVal());
312 
313         // now add 1 more
314 
315         DocumentSearchCriteria c6 = saveSearch(princ, null);
316 
317 
318         allUserOptions_after = userOptionsService.findByWorkflowUser(princ);
319 
320         // order should have wrapped around, now Holding0 is first, and contains c6 criteria
321         // still 5 entries
322         assertEquals(allUserOptions_before.size() + 5 + 1, allUserOptions_after.size());
323         assertEquals("DocSearch.LastSearch.Holding0,DocSearch.LastSearch.Holding4,DocSearch.LastSearch.Holding3,"
324                 + "DocSearch.LastSearch.Holding2,DocSearch.LastSearch.Holding1", userOptionsService.findByOptionId(
325                 "DocSearch.LastSearch.Order", princ).getOptionVal());
326         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c6), userOptionsService.findByOptionId(
327                 "DocSearch.LastSearch.Holding0", princ).getOptionVal());
328         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c5), userOptionsService.findByOptionId(
329                 "DocSearch.LastSearch.Holding4", princ).getOptionVal());
330         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c4), userOptionsService.findByOptionId(
331                 "DocSearch.LastSearch.Holding3", princ).getOptionVal());
332         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c3), userOptionsService.findByOptionId(
333                 "DocSearch.LastSearch.Holding2", princ).getOptionVal());
334         assertEquals(DocumentSearchInternalUtils.marshalDocumentSearchCriteria(c2), userOptionsService.findByOptionId(
335                 "DocSearch.LastSearch.Holding1", princ).getOptionVal());
336 
337         docSearchService.clearNamedSearches(princ);
338         assertEquals(0, userOptionsService.findByWorkflowUser(princ).size());
339     }
340 
341     private DocumentSearchCriteria saveSearch(String princ, String name) {
342         DocumentSearchCriteria dsc = createDocSearchCriteria(name);
343         docSearchService.saveSearch(princ, dsc);
344         return dsc;
345     }
346 
347     private DocumentSearchCriteria createDocSearchCriteria(String saveName) {
348         DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
349         builder.setApplicationDocumentId(RandomStringUtils.randomAlphanumeric(5));
350         builder.setApplicationDocumentStatus(RandomStringUtils.randomAlphanumeric(5));
351         builder.setApproverPrincipalName(RandomStringUtils.randomAlphanumeric(5));
352         builder.setApproverPrincipalId(RandomStringUtils.randomAlphanumeric(5));
353         builder.setDocumentId( RandomStringUtils.randomAlphanumeric(5));
354         builder.setDocumentTypeName(RandomStringUtils.randomAlphanumeric(5));
355         builder.setDocumentStatusCategories(Arrays.asList(DocumentStatusCategory.PENDING, DocumentStatusCategory.SUCCESSFUL));
356         builder.setDocumentStatuses(Arrays.asList(DocumentStatus.ENROUTE, DocumentStatus.INITIATED, DocumentStatus.SAVED));
357         builder.setInitiatorPrincipalName(RandomStringUtils.randomAlphanumeric(10));
358         builder.setInitiatorPrincipalId(RandomStringUtils.randomAlphanumeric(10));
359         builder.setMaxResults(500);
360         builder.setRouteNodeName(RandomStringUtils.randomAlphanumeric(5));
361         builder.setSaveName(saveName);
362         builder.setStartAtIndex(1);
363         builder.setTitle(RandomStringUtils.randomAlphanumeric(10));
364         builder.setGroupViewerId(RandomStringUtils.randomAlphanumeric(5));
365         builder.setViewerPrincipalName(RandomStringUtils.randomAlphanumeric(10));
366         builder.setViewerPrincipalId(RandomStringUtils.randomAlphanumeric(10));
367         builder.setRouteNodeLookupLogic(RouteNodeLookupLogic.EXACTLY);
368 
369         builder.setDateApplicationDocumentStatusChangedFrom(new DateTime());
370         builder.setDateApplicationDocumentStatusChangedTo(new DateTime());
371         builder.setDateApprovedFrom(new DateTime());
372         builder.setDateApprovedTo(new DateTime());
373         builder.setDateCreatedFrom(new DateTime());
374         builder.setDateCreatedTo(new DateTime());
375         builder.setDateFinalizedFrom(new DateTime());
376         builder.setDateFinalizedTo(new DateTime());
377         builder.setDateLastModifiedFrom(new DateTime());
378         builder.setDateLastModifiedTo(new DateTime());
379 
380         Map<String, List<String>> attrs = new HashMap<String, List<String>>();
381         for (int i = 1;i<6;i++) {
382             ArrayList<String> list = new ArrayList<String>();
383             for (int j = 1;j<6;j++) {
384                 list.add(RandomStringUtils.randomAlphanumeric(5));
385             }
386             attrs.put(RandomStringUtils.randomAlphanumeric(5), list);
387         }
388         builder.setDocumentAttributeValues(attrs);
389 
390         return builder.build();
391     }
392 }