001 /** 002 * Copyright 2005-2011 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.kew.docsearch; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.joda.time.DateTime; 020 import org.junit.Test; 021 import org.kuali.rice.kew.api.KewApiConstants; 022 import org.kuali.rice.kew.api.KewApiServiceLocator; 023 import org.kuali.rice.kew.api.WorkflowDocument; 024 import org.kuali.rice.kew.api.WorkflowDocumentFactory; 025 import org.kuali.rice.kew.api.document.DocumentContentUpdate; 026 import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition; 027 import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria; 028 import org.kuali.rice.kew.api.document.search.DocumentSearchResults; 029 import org.kuali.rice.kew.api.extension.ExtensionDefinition; 030 import org.kuali.rice.kew.docsearch.service.DocumentSearchService; 031 import org.kuali.rice.kew.docsearch.xml.StandardGenericXMLSearchableAttribute; 032 import org.kuali.rice.kew.doctype.bo.DocumentType; 033 import org.kuali.rice.kew.doctype.service.DocumentTypeService; 034 import org.kuali.rice.kew.impl.document.search.DocumentSearchCriteriaTranslatorImpl; 035 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 036 import org.kuali.rice.kew.routeheader.dao.DocumentRouteHeaderDAO; 037 import org.kuali.rice.kew.routeheader.service.RouteHeaderService; 038 import org.kuali.rice.kew.rule.bo.RuleAttribute; 039 import org.kuali.rice.kew.service.KEWServiceLocator; 040 import org.kuali.rice.kim.api.identity.Person; 041 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 042 import org.kuali.rice.krad.util.GlobalVariables; 043 import org.kuali.rice.krad.util.KRADConstants; 044 045 import java.math.BigDecimal; 046 import java.sql.Timestamp; 047 import java.util.ArrayList; 048 import java.util.Calendar; 049 import java.util.Collection; 050 import java.util.Collections; 051 import java.util.HashMap; 052 import java.util.List; 053 import java.util.Map; 054 055 import static org.junit.Assert.*; 056 057 /** 058 * Tests the StandardGenericXMLSearchableAttribute. 059 * 060 * KULWF-654: Tests the resolution to this issue by configuring a CustomActionListAttribute as well as a 061 * searchable attribute. 062 */ 063 public class SearchableAttributeTest extends DocumentSearchTestBase { 064 065 protected void loadTestData() throws Exception { 066 loadXmlFile("SearchAttributeConfig.xml"); 067 loadXmlFile("SearchableTrimTest.xml"); 068 } 069 070 // private SearchAttributeCriteriaComponent createSearchAttributeCriteriaComponent(String key,String value,Boolean isLowerBoundValue,DocumentType docType) { 071 // String formKey = (isLowerBoundValue == null) ? key : ((isLowerBoundValue != null && isLowerBoundValue.booleanValue()) ? SearchableAttributeOld.RANGE_LOWER_BOUND_PROPERTY_PREFIX : SearchableAttributeOld.RANGE_UPPER_BOUND_PROPERTY_PREFIX); 072 // String savedKey = key; 073 // SearchAttributeCriteriaComponent sacc = new SearchAttributeCriteriaComponent(formKey,value,savedKey); 074 // Field field = getFieldByFormKey(docType, formKey); 075 // if (field != null) { 076 // sacc.setSearchableAttributeValue(DocSearchUtils.getSearchableAttributeValueByDataTypeString(field.getFieldDataType())); 077 // sacc.setRangeSearch(field.isMemberOfRange()); 078 // sacc.setAllowWildcards(field.isAllowingWildcards()); 079 // sacc.setAutoWildcardBeginning(field.isAutoWildcardAtBeginning()); 080 // sacc.setAutoWildcardEnd(field.isAutoWildcardAtEnding()); 081 // sacc.setCaseSensitive(field.isCaseSensitive()); 082 // sacc.setSearchInclusive(field.isInclusive()); 083 // sacc.setSearchable(field.isSearchable()); 084 // sacc.setCanHoldMultipleValues(Field.MULTI_VALUE_FIELD_TYPES.contains(field.getFieldType())); 085 // } 086 // return sacc; 087 // } 088 // 089 // private Field getFieldByFormKey(DocumentType docType, String formKey) { 090 // if (docType == null) { 091 // return null; 092 // } 093 // for (SearchableAttributeOld searchableAttribute : docType.getSearchableAttributesOld()) { 094 // for (Row row : searchableAttribute.getSearchingRows()) { 095 // for (Field field : row.getFields()) { 096 // if (field.getPropertyName().equals(formKey)) { 097 // return field; 098 // } 099 // } 100 // } 101 // } 102 // return null; 103 // } 104 105 /** 106 * This tests the ability to get the searchableAttributeValues directly without going through the document. 107 */ 108 @Test public void testSearchableAttributeSearch()throws Exception { 109 String documentTypeName = "SearchDocType"; 110 String userNetworkId = "rkirkend"; 111 WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId(userNetworkId), documentTypeName); 112 workflowDocument.setTitle("Routing style"); 113 workflowDocument.route("routing this document."); 114 115 workflowDocument = WorkflowDocumentFactory.loadDocument(getPrincipalId(userNetworkId), workflowDocument.getDocumentId()); 116 DocumentRouteHeaderValue doc = KEWServiceLocator.getRouteHeaderService().getRouteHeader(workflowDocument.getDocumentId()); 117 118 // HACK: we are cheating, but this functionality was removed from the service apis, so we hit the DAO directly 119 DocumentRouteHeaderDAO dao = KEWServiceLocator.getBean("enDocumentRouteHeaderDAO"); 120 Collection<SearchableAttributeValue> allValues = dao.findSearchableAttributeValues(workflowDocument.getDocumentId()); 121 assertEquals("Wrong number of searchable attributes", 4, allValues.size()); 122 123 for (SearchableAttributeValue attributeValue: allValues) { 124 if (attributeValue instanceof SearchableAttributeStringValue) { 125 SearchableAttributeStringValue realValue = (SearchableAttributeStringValue) attributeValue; 126 127 for(String value:getRouteHeaderService().getSearchableAttributeStringValuesByKey(doc.getDocumentId(), realValue.getSearchableAttributeKey())){ 128 assertEquals("Assert that the values are the same", value, attributeValue.getSearchableAttributeValue()); 129 } 130 131 } else if (attributeValue instanceof SearchableAttributeLongValue) { 132 SearchableAttributeLongValue realValue = (SearchableAttributeLongValue) attributeValue; 133 for(Long value:getRouteHeaderService().getSearchableAttributeLongValuesByKey(doc.getDocumentId(), realValue.getSearchableAttributeKey())){ 134 assertEquals("Assert that the values are the same", value, attributeValue.getSearchableAttributeValue()); 135 } 136 } else if (attributeValue instanceof SearchableAttributeFloatValue) { 137 SearchableAttributeFloatValue realValue = (SearchableAttributeFloatValue) attributeValue; 138 for(BigDecimal value:getRouteHeaderService().getSearchableAttributeFloatValuesByKey(doc.getDocumentId(), realValue.getSearchableAttributeKey())){ 139 assertEquals("Assert that the values are the same", value, attributeValue.getSearchableAttributeValue()); 140 } 141 142 } else if (attributeValue instanceof SearchableAttributeDateTimeValue) { 143 SearchableAttributeDateTimeValue realValue = (SearchableAttributeDateTimeValue) attributeValue; 144 assertEquals("The only DateTime attribute that should have been added has key '" + TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY + "'", TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY, realValue.getSearchableAttributeKey()); 145 146 Calendar testDate = Calendar.getInstance(); 147 testDate.setTimeInMillis(realValue.getSearchableAttributeValue().getTime()); 148 testDate.set(Calendar.SECOND, 0); 149 testDate.set(Calendar.MILLISECOND, 0); 150 151 for(Timestamp value:getRouteHeaderService().getSearchableAttributeDateTimeValuesByKey(doc.getDocumentId(), realValue.getSearchableAttributeKey())){ 152 Calendar attributeDate = Calendar.getInstance(); 153 attributeDate.setTimeInMillis(value.getTime()); 154 attributeDate.set(Calendar.SECOND, 0); 155 attributeDate.set(Calendar.MILLISECOND, 0); 156 157 assertEquals("The month value for the searchable attribute is wrong",testDate.get(Calendar.MONTH),attributeDate.get(Calendar.MONTH)); 158 assertEquals("The date value for the searchable attribute is wrong",testDate.get(Calendar.DATE),attributeDate.get(Calendar.DATE)); 159 assertEquals("The year value for the searchable attribute is wrong",testDate.get(Calendar.YEAR),attributeDate.get(Calendar.YEAR)); 160 } 161 162 } else { 163 fail("Searchable Attribute Value base class should be one of the four checked always"); 164 } 165 } 166 } 167 168 protected RouteHeaderService getRouteHeaderService(){ 169 RouteHeaderService rRet = KEWServiceLocator.getRouteHeaderService(); 170 return rRet; 171 } 172 173 protected String getPrincipalId(String networkId){ 174 return KimApiServiceLocator.getPersonService().getPersonByPrincipalName(networkId).getPrincipalId(); 175 } 176 177 @Test public void testCustomSearchableAttributesWithDataType() throws Exception { 178 String documentTypeName = "SearchDocType"; 179 DocumentType docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName); 180 String userNetworkId = "rkirkend"; 181 WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId(userNetworkId), documentTypeName); 182 workflowDocument.setTitle("Routing style"); 183 workflowDocument.route("routing this document."); 184 185 workflowDocument = WorkflowDocumentFactory.loadDocument(getPrincipalId(userNetworkId), workflowDocument.getDocumentId()); 186 DocumentRouteHeaderValue doc = KEWServiceLocator.getRouteHeaderService().getRouteHeader(workflowDocument.getDocumentId()); 187 188 DocumentSearchService docSearchService = (DocumentSearchService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_SEARCH_SERVICE); 189 Person user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(userNetworkId); 190 191 DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create(); 192 criteria.setDocumentTypeName(documentTypeName); 193 addSearchableAttribute(criteria, TestXMLSearchableAttributeString.SEARCH_STORAGE_KEY, 194 TestXMLSearchableAttributeString.SEARCH_STORAGE_VALUE); 195 DocumentSearchResults results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 196 assertEquals("Search results should have one document.", 1, results.getSearchResults().size()); 197 198 criteria = DocumentSearchCriteria.Builder.create(); 199 criteria.setDocumentTypeName(documentTypeName); 200 addSearchableAttribute(criteria, TestXMLSearchableAttributeString.SEARCH_STORAGE_KEY, "fred"); 201 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 202 assertEquals("Search results should be empty.", 0, results.getSearchResults().size()); 203 204 criteria = DocumentSearchCriteria.Builder.create(); 205 criteria.setDocumentTypeName(documentTypeName); 206 addSearchableAttribute(criteria, "fakeproperty", "doesntexist"); 207 try { 208 docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 209 fail("Search results should be throwing a validation exception for use of non-existant searchable attribute"); 210 } catch (RuntimeException e) {} 211 212 criteria = DocumentSearchCriteria.Builder.create(); 213 criteria.setDocumentTypeName(documentTypeName); 214 addSearchableAttribute(criteria, TestXMLSearchableAttributeLong.SEARCH_STORAGE_KEY, 215 TestXMLSearchableAttributeLong.SEARCH_STORAGE_VALUE.toString()); 216 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 217 assertEquals("Search results should have one document.", 1, results.getSearchResults().size()); 218 219 criteria = DocumentSearchCriteria.Builder.create(); 220 criteria.setDocumentTypeName(documentTypeName); 221 addSearchableAttribute(criteria, TestXMLSearchableAttributeLong.SEARCH_STORAGE_KEY, "1111111"); 222 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 223 assertEquals("Search results should be empty.", 0, results.getSearchResults().size()); 224 225 criteria = DocumentSearchCriteria.Builder.create(); 226 criteria.setDocumentTypeName(documentTypeName); 227 addSearchableAttribute(criteria, "fakeymcfakefake", "99999999"); 228 try { 229 docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 230 fail("Search results should be throwing a validation exception for use of non-existant searchable attribute"); 231 } catch (RuntimeException e) {} 232 233 criteria = DocumentSearchCriteria.Builder.create(); 234 criteria.setDocumentTypeName(documentTypeName); 235 addSearchableAttribute(criteria, TestXMLSearchableAttributeFloat.SEARCH_STORAGE_KEY, 236 TestXMLSearchableAttributeFloat.SEARCH_STORAGE_VALUE.toString()); 237 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 238 assertEquals("Search results should have one document.", 1, results.getSearchResults().size()); 239 240 criteria = DocumentSearchCriteria.Builder.create(); 241 criteria.setDocumentTypeName(documentTypeName); 242 addSearchableAttribute(criteria, TestXMLSearchableAttributeFloat.SEARCH_STORAGE_KEY, "215.3548"); 243 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 244 assertEquals("Search results should be empty.", 0, results.getSearchResults().size()); 245 246 criteria = DocumentSearchCriteria.Builder.create(); 247 criteria.setDocumentTypeName(documentTypeName); 248 addSearchableAttribute(criteria, "fakeylostington", "9999.9999"); 249 try { 250 docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 251 fail("Search results should be throwing a validation exception for use of non-existant searchable attribute"); 252 } catch (RuntimeException e) {} 253 254 criteria = DocumentSearchCriteria.Builder.create(); 255 criteria.setDocumentTypeName(documentTypeName); 256 addSearchableAttribute(criteria, TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY, 257 DocumentSearchInternalUtils.getDisplayValueWithDateOnly(new Timestamp( 258 TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_VALUE_IN_MILLS))); 259 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 260 assertEquals("Search results should have one document.", 1, results.getSearchResults().size()); 261 262 criteria = DocumentSearchCriteria.Builder.create(); 263 criteria.setDocumentTypeName(documentTypeName); 264 addSearchableAttribute(criteria, TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY, "07/06/1979"); 265 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 266 assertEquals("Search results should be empty.", 0, results.getSearchResults().size()); 267 268 criteria = DocumentSearchCriteria.Builder.create(); 269 criteria.setDocumentTypeName(documentTypeName); 270 addSearchableAttribute(criteria, "lastingsfakerson", "07/06/2007"); 271 try { 272 docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 273 fail("Search results should be throwing a validation exception for use of non-existant searchable attribute"); 274 } catch (RuntimeException e) {} 275 } 276 277 /** 278 * Tests searching documents with searchable attributes 279 * @throws org.kuali.rice.kew.api.exception.WorkflowException 280 */ 281 @Test public void testSearchAttributesAcrossDocumentTypeVersions() throws Exception { 282 // first test searching for an initial version of the doc which does not have a searchable attribute 283 loadXmlFile("testdoc0.xml"); 284 285 String documentTypeName = "SearchDoc"; 286 WorkflowDocument doc = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("arh14"), documentTypeName); 287 DocumentType docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName); 288 doc.route("routing"); 289 290 DocumentSearchService docSearchService = (DocumentSearchService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_SEARCH_SERVICE); 291 292 DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create(); 293 criteria.setDocumentTypeName(documentTypeName); 294 criteria.setDateCreatedFrom(new DateTime(2004, 1, 1, 0, 0)); 295 296 Person user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName("arh14"); 297 DocumentSearchResults results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 298 assertEquals(1, results.getSearchResults().size()); 299 300 // now upload the new version with a searchable attribute 301 loadXmlFile("testdoc1.xml"); 302 docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName); 303 304 // route a new doc 305 doc = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("arh14"), documentTypeName); 306 doc.route("routing"); 307 308 // with no attribute criteria, both docs should be found 309 criteria = DocumentSearchCriteria.Builder.create(); 310 criteria.setDocumentTypeName(documentTypeName); 311 criteria.setDateCreatedFrom(new DateTime(2004, 1, 1, 0, 0)); 312 313 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 314 assertEquals(2, results.getSearchResults().size()); 315 316 // search with specific SearchableAttributeOld value 317 criteria = DocumentSearchCriteria.Builder.create(); 318 criteria.setDocumentTypeName(documentTypeName); 319 criteria.setDateCreatedFrom(new DateTime(2004, 1, 1, 0, 0)); 320 addSearchableAttribute(criteria, "MockSearchableAttributeKey", "MockSearchableAttributeValue"); 321 322 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 323 assertEquals(1, results.getSearchResults().size()); 324 325 // search with any SearchableAttributeOld value 326 criteria = DocumentSearchCriteria.Builder.create(); 327 criteria.setDocumentTypeName(documentTypeName); 328 criteria.setDateCreatedFrom(new DateTime(2004, 1, 1, 0, 0)); 329 330 results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build()); 331 332 assertEquals(2, results.getSearchResults().size()); 333 } 334 335 /** 336 * Tests the usage of wildcards on searchable attributes of varying data types. 337 * Note that the bounds of ".."-related search expressions will not throw an exception if the lower bound is greater than the upper bound; 338 * instead, such an expression will simply return zero results. 339 * @throws Exception 340 */ 341 @Test public void testWildcardsOnSearchableAttributes() throws Exception { 342 String documentTypeName = "WildcardTestDocType"; 343 DocumentType docType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName); 344 String principalName = "rkirkend"; 345 String principalId = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(principalName).getPrincipalId(); 346 String[][] searchableAttributeValuesAsStrings = { {"testString", "9984", "38.1357", "06/24/2009"}, 347 {"anotherStr", "33", "80000.65432", "07/08/2010"}, {"MoreText", "432", "-0.765", "12/12/2012"} }; 348 349 // Route some documents containing the searchable attribute values given by the above array. 350 for (int i = 0; i < searchableAttributeValuesAsStrings.length; i++) { 351 WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(principalId, documentTypeName); 352 353 // Add the string searchable attribute. 354 WorkflowAttributeDefinition.Builder wcStringXMLDef = WorkflowAttributeDefinition.Builder.create("XMLSearchableAttributeWildcardString"); 355 wcStringXMLDef.addPropertyDefinition("xmlSearchableAttributeWildcardString", searchableAttributeValuesAsStrings[i][0]); 356 workflowDocument.addSearchableDefinition(wcStringXMLDef.build()); 357 // Add the long searchable attribute. 358 WorkflowAttributeDefinition.Builder wcLongXMLDef = WorkflowAttributeDefinition.Builder.create("XMLSearchableAttributeWildcardLong"); 359 wcLongXMLDef.addPropertyDefinition("xmlSearchableAttributeWildcardLong", searchableAttributeValuesAsStrings[i][1]); 360 workflowDocument.addSearchableDefinition(wcLongXMLDef.build()); 361 // Add the float searchable attribute. 362 WorkflowAttributeDefinition.Builder wcFloatXMLDef = WorkflowAttributeDefinition.Builder.create("XMLSearchableAttributeWildcardFloat"); 363 wcFloatXMLDef.addPropertyDefinition("xmlSearchableAttributeWildcardFloat", searchableAttributeValuesAsStrings[i][2]); 364 workflowDocument.addSearchableDefinition(wcFloatXMLDef.build()); 365 // Add the datetime searchable attribute. 366 WorkflowAttributeDefinition.Builder wcDatetimeXMLDef = WorkflowAttributeDefinition.Builder.create("XMLSearchableAttributeWildcardDatetime"); 367 wcDatetimeXMLDef.addPropertyDefinition("xmlSearchableAttributeWildcardDatetime", searchableAttributeValuesAsStrings[i][3]); 368 workflowDocument.addSearchableDefinition(wcDatetimeXMLDef.build()); 369 370 workflowDocument.setTitle("Search Def Test Doc " + i); 371 workflowDocument.route("routing search def doc " + i); 372 } 373 374 // Ensure that wildcards work on searchable string attributes. Note that this search should be case-insensitive by default. 375 // Also note that this should be the only case where the string-specific wildcards ("!", "?", and "*") should be working, unless 376 // they are being used in a range expression. 377 assertSearchableAttributeWildcardsWork(docType, principalId, "xmlSearchableAttributeWildcardString", 378 new String[] {"TESTSTRING|moretext", "!MoreText" , "!anotherStr!testString", "!anotherStr&&!MoreText" , "!SomeString" , 379 "*str*" , "More????" , "*e*n?" , "???String" , "*te*&&!????String", "!test??????" , "anotherStr..MoreText", 380 "testString..MoreText" , ">=testString", "<=anotherStr|>MoreText", "<=testString&&!anotherStr", ">=abc" , "<=anotherOne&&>=text", 381 ">=More????" , "<=*test*"}, 382 new int[] {2 , 2 , 1 , 1 , 3 , 383 2 , 1 , 1 , 0 , 1 , 2 , 2 /*1*/ , 384 -1 , 1 , 2 , 2 , 3 , 0 , 385 2 , 2}); 386 387 // ensure multiple values work 388 assertSearchableAttributeMultiplesWork(docType, principalId, "xmlSearchableAttributeWildcardString", 389 new String[][] { {"testString"}, {"anotherStr"}, {"MoreText"}, {"testString", "anotherStr"}, {"testString", "MoreText"}, {"anotherStr", "MoreText"}, {"testString", "anotherStr", "MoreText"}, {"monkey"}, {"monkey", "giraffe"}, {"monkey", "testString"} }, 390 new int[] { 1, 1, 1, 2, 2, 2, 3, 0, 0, 1 }); 391 392 // Ensure that wildcards work on searchable long attributes, and ensure the string-specific wildcards are not being utilized. 393 assertSearchableAttributeWildcardsWork(docType, principalId, "xmlSearchableAttributeWildcardLong", 394 new String[] {"99??", "*2" , "!33" , "<9984", ">=433", "<34", ">=432", ">=34&&<9984", "<100000&&>=20", ">=9984&&<33", "431..<9985", 395 "9999..1", "<432|>432", ">=9000|<=100", "!", ">=-76"}, 396 new int[] {-1 , -1 , 2 , 2 , 1 , 1 , 2 , 1 , 3 , 0 , 2 /*1*/ , 397 -1 , 2 , 2 , -1 , 3}); 398 399 // ensure multiple values work 400 assertSearchableAttributeMultiplesWork(docType, principalId, "xmlSearchableAttributeWildcardLong", 401 new String[][] { {"9984"}, {"33"}, {"432"}, {"9984", "33"}, {"9984", "432"}, {"33", "432"}, {"9984", "33", "432"}, {"7"}, {"7", "4488"}, {"7", "9984"} }, 402 new int[] { 1, 1, 1, 2, 2, 2, 3, 0, 0, 1 }); 403 404 // Ensure that wildcards work on searchable float attributes, and ensure the string-specific wildcards are not being utilized. 405 assertSearchableAttributeWildcardsWork(docType, principalId, "xmlSearchableAttributeWildcardFloat", 406 new String[] {"38.1???", "!-0.765", "*80*" , "<80000.65432" , ">=0" , "<-0.763", ">=38.1357", "<38.1358", "<-0.5|>0.5", ">=-0.765&&<=-0.765", ">=38.1358&&<80000.65432", 407 "-50..<50" , "100..10", "<38.1358|>=38.1357" , ">=123.4567|<0.11", "-1.1..<38.1357&&<3.3"}, 408 new int[] {-1 , 2 , -1 , 2 , 2 , 1 , 2 , 2 , 3 , 1 , 0 , 409 2 , -1 , 3 , 2 , 1}); 410 411 // ensure multiple values work 412 assertSearchableAttributeMultiplesWork(docType, principalId, "xmlSearchableAttributeWildcardFloat", 413 new String[][] { {"38.1357"}, {"80000.65432"}, {"-0.765"}, {"38.1357", "80000.65432"}, {"38.1357", "-0.765"}, {"80000.65432", "-0.765"}, {"38.1357", "80000.65432", "-0.765"}, {"3.1415928"}, {"3.1415928", "4488.0"}, {"3.1415928", "38.1357"} }, 414 new int[] { 1, 1, 1, 2, 2, 2, 3, 0, 0, 1 }); 415 416 417 // Ensure that wildcards work on searchable datetime attributes, and ensure the string-specific wildcards are not being utilized. 418 /* 06/24/2009, 07/08/2010, 12/12/2012 */ 419 assertSearchableAttributeWildcardsWork(docType, principalId, "xmlSearchableAttributeWildcardDatetime", 420 new String[] {"??/??/20??" , "12/12/20*" , "!07/08/2010" , ">=06/25/2009", "<07/08/2010", ">=12/12/2012", "<05/06/2011", ">=06/25/2009&&<07/09/2010", 421 ">=01/01/2001&&<06/24/2009", "11/29/1990..<12/31/2009" , "12/13/2100..<08/09/1997", 422 "<06/24/2009|>=12/12/2012" , "<06/25/2009|>=07/09/2010", ">02/31/2011"}, 423 new int[] {-1 , -1 , 2 /* supports NOT operator*/, 2 , 1 , 1 , 2 , 1 , 424 0 , 1 , -1 , 425 1 , 2 , -1}); 426 427 // ensure multiple values work 428 assertSearchableAttributeMultiplesWork(docType, principalId, "xmlSearchableAttributeWildcardDatetime", 429 new String[][] { {"06/24/2009"}, {"07/08/2010"}, {"12/12/2012"}, {"06/24/2009", "07/08/2010"}, {"06/24/2009", "12/12/2012"}, {"07/08/2010", "12/12/2012"}, {"06/24/2009", "07/08/2010", "12/12/2012"}, {"12/20/2012"}, {"12/20/2012", "11/09/2009"}, {"12/20/2012", "12/12/2012"} }, 430 new int[] { 1, 1, 1, 2, 2, 2, 3, 0, 0, 1 }); 431 432 } 433 434 /** 435 * A convenience method for testing wildcards on searchable attributes. 436 * 437 * @param docType The document type containing the attributes. 438 * @param principalId The ID of the user performing the search. 439 * @param fieldDefKey The name of the field given by the field definition on the searchable attribute. 440 * @param searchValues The wildcard-filled search strings to test. 441 * @param resultSizes The number of expected documents to be returned by the search; use -1 to indicate that an error should have occurred. 442 * @throws Exception 443 */ 444 private void assertSearchableAttributeWildcardsWork(DocumentType docType, String principalId, String fieldDefKey, String[] searchValues, 445 int[] resultSizes) throws Exception { 446 DocumentSearchCriteria.Builder criteria = null; 447 DocumentSearchResults results = null; 448 DocumentSearchService docSearchService = KEWServiceLocator.getDocumentSearchService(); 449 for (int i = 0; i < resultSizes.length; i++) { 450 criteria = DocumentSearchCriteria.Builder.create(); 451 criteria.setDocumentTypeName(docType.getName()); 452 addSearchableAttribute(criteria, fieldDefKey, searchValues[i]); 453 try { 454 results = docSearchService.lookupDocuments(principalId, criteria.build()); 455 if (resultSizes[i] < 0) { 456 fail(fieldDefKey + "'s search at loop index " + i + " should have thrown an exception"); 457 } 458 if(resultSizes[i] != results.getSearchResults().size()){ 459 assertEquals(fieldDefKey + "'s search results at loop index " + i + " returned the wrong number of documents.", resultSizes[i], results.getSearchResults().size()); 460 } 461 } catch (Exception ex) { 462 if (resultSizes[i] >= 0) { 463 ex.printStackTrace(); 464 fail(fieldDefKey + "'s search at loop index " + i + " should not have thrown an exception"); 465 } 466 } 467 GlobalVariables.clear(); 468 } 469 } 470 471 /** 472 * A convenience method for testing multiple value fields on searchable attributes. 473 * 474 * @param docType The document type containing the attributes. 475 * @param principalId The ID of the user performing the search. 476 * @param fieldDefKey The name of the field given by the field definition on the searchable attribute. 477 * @param searchValues The wildcard-filled search strings to test. 478 * @param resultSizes The number of expected documents to be returned by the search; use -1 to indicate that an error should have occurred. 479 * @throws Exception 480 */ 481 private void assertSearchableAttributeMultiplesWork(DocumentType docType, String principalId, String fieldDefKey, String[][] searchValues, 482 int[] resultSizes) throws Exception { 483 DocumentSearchCriteria.Builder criteria = null; 484 DocumentSearchResults results = null; 485 DocumentSearchService docSearchService = KEWServiceLocator.getDocumentSearchService(); 486 for (int i = 0; i < resultSizes.length; i++) { 487 criteria = DocumentSearchCriteria.Builder.create(); 488 criteria.setDocumentTypeName(docType.getName()); 489 addSearchableAttribute(criteria, fieldDefKey, searchValues[i]); 490 try { 491 results = docSearchService.lookupDocuments(principalId, criteria.build()); 492 if (resultSizes[i] < 0) { 493 fail(fieldDefKey + "'s search at loop index " + i + " should have thrown an exception"); 494 } 495 if(resultSizes[i] != results.getSearchResults().size()){ 496 assertEquals(fieldDefKey + "'s search results at loop index " + i + " returned the wrong number of documents.", resultSizes[i], results.getSearchResults().size()); 497 } 498 } catch (Exception ex) { 499 if (resultSizes[i] >= 0) { 500 fail(fieldDefKey + "'s search at loop index " + i + " should not have thrown an exception"); 501 } 502 } 503 GlobalVariables.clear(); 504 } 505 } 506 507 508 509 /** 510 * Per KULRICE-3681, tests that StandardGenericXMLSearchableAttribute throws no cast class exception when it shouldn't 511 */ 512 @Test 513 public void testValidateUserSearchInputsNoCast() { 514 StandardGenericXMLSearchableAttribute searchableAttribute = new StandardGenericXMLSearchableAttribute(); 515 ExtensionDefinition extensionDefinition = KewApiServiceLocator.getExtensionRepositoryService().getExtensionByName("SearchableAttributeVisible"); 516 517 DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create(); 518 Map<String, List<String>> simpleParamMap = new HashMap<String, List<String>>(); 519 simpleParamMap.put("givenname", Collections.singletonList("test")); 520 criteria.setDocumentAttributeValues(simpleParamMap); 521 List errors = new ArrayList(); 522 Exception caughtException = null; 523 try { 524 errors = searchableAttribute.validateDocumentAttributeCriteria(extensionDefinition, criteria.build()); 525 } catch (RuntimeException re) { 526 caughtException = re; 527 } 528 assertNull("Found exception "+caughtException, caughtException); 529 assertTrue("Found errors "+errors, (errors.size() == 0)); 530 531 Map<String, List<String>> listParamMap = new HashMap<String, List<String>>(); 532 List<String> multipleValues = new ArrayList<String>(); 533 multipleValues.add("testone"); 534 multipleValues.add("testtwo"); 535 listParamMap.put("givenname", multipleValues); 536 criteria.setDocumentAttributeValues(listParamMap); 537 errors = new ArrayList(); 538 caughtException = null; 539 try { 540 errors = searchableAttribute.validateDocumentAttributeCriteria(extensionDefinition, criteria.build()); 541 } catch (RuntimeException re) { 542 caughtException = re; 543 } 544 assertNull("Found exception "+caughtException, caughtException); 545 assertTrue("Found errors "+errors, (errors.size() == 0)); 546 547 } 548 549 @Test 550 public void testSearchableAttributeTrim() { 551 RuleAttribute trimAttribute = KEWServiceLocator.getRuleAttributeService().findByName("TrimSearchableAttribute"); 552 assert(trimAttribute.getName().equals("TrimSearchableAttribute")); 553 assert(trimAttribute.getResourceDescriptor().equals("org.kuali.rice.kew.docsearch.xml.StandardGenericXMLSearchableAttribute")); 554 assert(trimAttribute.getLabel().equals("Unit111")); 555 assert(trimAttribute.getType().equals("SearchableXmlAttribute")); 556 assert(trimAttribute.getDescription().equals("Unit111")); 557 assert(trimAttribute.getApplicationId().equals("NSTrimSearchableTest")); 558 //System.out.println(trimAttribute.getName()); 559 } 560 561 @Test 562 public void testXmlGeneration() { 563 loadXmlFile("testdoc1.xml"); 564 WorkflowAttributeDefinition searchableDefinition = WorkflowAttributeDefinition.Builder.create("SearchAttribute").build(); 565 DocumentContentUpdate.Builder documentContentUpdateBuilder = DocumentContentUpdate.Builder.create(); 566 documentContentUpdateBuilder.getSearchableDefinitions().add(searchableDefinition); 567 WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId("ewestfal"), "SearchDoc", null, documentContentUpdateBuilder.build()); 568 workflowDocument.route(""); 569 assertTrue(workflowDocument.isFinal()); 570 assertEquals(StringUtils.deleteWhitespace("<" + KewApiConstants.SEARCHABLE_CONTENT_ELEMENT + ">" + MockSearchableAttribute.SEARCH_CONTENT + "</" + KewApiConstants.SEARCHABLE_CONTENT_ELEMENT + ">"), 571 StringUtils.deleteWhitespace(workflowDocument.getDocumentContent().getSearchableContent())); 572 } 573 574 @Test 575 public void testAttributeRangeFieldGeneration() { 576 String documentTypeName = "SearchDocType"; 577 DocumentType docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName); 578 String userNetworkId = "rkirkend"; 579 WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId(userNetworkId), documentTypeName); 580 workflowDocument.setTitle("Routing style"); 581 workflowDocument.route("routing this document."); 582 583 DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create(); 584 criteria.setDocumentTypeName(documentTypeName); 585 criteria.setDateApprovedFrom(new DateTime(2010, 1, 1, 0, 0)); 586 criteria.setDateApprovedTo(new DateTime(2011, 1, 1, 0, 0)); 587 String fieldValue = ">= " + DocumentSearchInternalUtils.getDisplayValueWithDateOnly(new Timestamp(TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_VALUE_IN_MILLS)); 588 addSearchableAttribute(criteria, TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY, fieldValue); 589 590 Map<String, String[]> fields = new DocumentSearchCriteriaTranslatorImpl().translateCriteriaToFields(criteria.build()); 591 System.err.println(fields); 592 String lowerBoundField = KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX + KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY; 593 String upperBoundField = KewApiConstants.DOCUMENT_ATTRIBUTE_FIELD_PREFIX + TestXMLSearchableAttributeDateTime.SEARCH_STORAGE_KEY; 594 595 assertNotNull(fields.get(lowerBoundField)); 596 assertNotNull(fields.get(upperBoundField)); 597 assertNotNull(fields.get(lowerBoundField)[0]); 598 assertNull(fields.get(upperBoundField)[0]); 599 600 assertEquals(DocumentSearchInternalUtils.getDisplayValueWithDateOnly(new Timestamp(new DateTime(2007, 3, 15, 0, 0).toDateTime().getMillis())), fields.get(lowerBoundField)[0]); 601 } 602 603 }