001    /**
002     * Copyright 2005-2013 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.junit.Test;
020    import org.kuali.rice.core.api.uif.RemotableAttributeError;
021    import org.kuali.rice.core.api.uif.RemotableAttributeField;
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.DocumentStatus;
026    import org.kuali.rice.kew.api.document.DocumentWithContent;
027    import org.kuali.rice.kew.api.document.attribute.DocumentAttribute;
028    import org.kuali.rice.kew.api.document.attribute.DocumentAttributeDataType;
029    import org.kuali.rice.kew.api.document.attribute.DocumentAttributeFactory;
030    import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition;
031    import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
032    import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
033    import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
034    import org.kuali.rice.kew.api.extension.ExtensionDefinition;
035    import org.kuali.rice.kew.doctype.bo.DocumentType;
036    import org.kuali.rice.kew.framework.document.attribute.SearchableAttribute;
037    import org.kuali.rice.kew.framework.document.search.DocumentSearchCustomizerBase;
038    import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValue;
039    import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValues;
040    import org.kuali.rice.kew.service.KEWServiceLocator;
041    import org.kuali.rice.kew.test.KEWTestCase;
042    
043    import java.util.ArrayList;
044    import java.util.Collections;
045    import java.util.List;
046    
047    import static org.junit.Assert.*;
048    
049    /**
050     * An integration test for the DocumentSearchCustomizer class.  Includes tests on various aspects of the customzations
051     * that class provides, attempting to use the high-level document search apis to exercise it.
052     *
053     * @author Kuali Rice Team (rice.collab@kuali.org)
054     */
055    public class DocumentSearchCustomizerTest extends KEWTestCase {
056    
057        @Override
058            protected void loadTestData() throws Exception {
059            loadXmlFile("DocumentSearchCustomizerTest.xml");
060        }
061    
062        @Test
063        public void testCustomizeCriteria() throws Exception {
064    
065            String ewestfal = getPrincipalIdForName("ewestfal");
066            DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
067    
068            // first check a full doc search, should return no results
069            DocumentSearchResults results = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(ewestfal, builder.build());
070            assertTrue(results.getSearchResults().isEmpty());
071            assertFalse(results.getCriteria().getDocumentStatuses().contains(DocumentStatus.FINAL));
072    
073            // now check a document search against the "DocumentSearchCustomizerTest" document type, it is configured with the
074            // CustomizeCriteria customizer
075            builder.setDocumentTypeName("DocumentSearchCustomizerTest");
076    
077            results = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(ewestfal, builder.build());
078            assertTrue(results.getSearchResults().isEmpty());
079            DocumentSearchCriteria resultCriteria = results.getCriteria();
080            assertTrue("Document Statuses should have contained FINAL, instead contains: " + resultCriteria
081                    .getDocumentStatuses(), resultCriteria.getDocumentStatuses().contains(DocumentStatus.FINAL));
082    
083            // now route an instance of the CustomizeCriteria document type to FINAL
084            WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"),
085                    "DocumentSearchCustomizerTest");
086            document.route("");
087            assertTrue(document.isFinal());
088    
089            // now run another search, we should get back one result this time
090            results = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(ewestfal, builder.build());
091            assertEquals(1, results.getSearchResults().size());
092            assertEquals(document.getDocumentId(), results.getSearchResults().get(0).getDocument().getDocumentId());
093        }
094    
095        @Test
096        public void testCustomizeClearCriteria() throws Exception {
097    
098            // grab a couple of document types, the first is the TestDocumentType which has no DocumentSearchCustomizer
099            // configured on it, the second in our Document Type for this integration test which is configured with the
100            // Customizer
101    
102            DocumentType testDocumentType = KEWServiceLocator.getDocumentTypeService().findByName("TestDocumentType");
103            assertNotNull(testDocumentType);
104            DocumentType customizedDocumentType = KEWServiceLocator.getDocumentTypeService().findByName("DocumentSearchCustomizerTest");
105            assertNotNull(customizedDocumentType);
106    
107            // first set document id and application document id on a criteria and clear it using the TestDocumentType, it
108            // should clear out both
109    
110            DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
111            builder.setDocumentId("12345");
112            builder.setApplicationDocumentId("54321");
113            DocumentSearchCriteria clearedCriteria = KEWServiceLocator.getDocumentSearchService().clearCriteria(testDocumentType, builder.build());
114            assertNull(clearedCriteria.getDocumentId());
115            assertNull(clearedCriteria.getApplicationDocumentId());
116    
117            // now clear the same criteria with the customized document type, it should clear out the document id but
118            // preserve the application document id
119    
120            clearedCriteria = KEWServiceLocator.getDocumentSearchService().clearCriteria(customizedDocumentType, builder.build());
121            assertNull(clearedCriteria.getDocumentId());
122            assertEquals("54321", clearedCriteria.getApplicationDocumentId());
123        }
124    
125        @Test
126        public void testCustomizeResults() throws Exception {
127    
128            // route an instance of the CustomizeCriteria document type to FINAL
129    
130            String ewestfal = getPrincipalIdForName("ewestfal");
131            WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"),
132                    "DocumentSearchCustomizerTest");
133            document.route("");
134            assertTrue(document.isFinal());
135    
136            // check that the document attributes get indexed properly
137    
138            List<String> attributeValues = KewApiServiceLocator.getWorkflowDocumentService().getSearchableAttributeStringValuesByKey(document.getDocumentId(), "myAttribute");
139            assertEquals(1, attributeValues.size());
140            assertEquals("myValue", attributeValues.get(0));
141            attributeValues = KewApiServiceLocator.getWorkflowDocumentService().getSearchableAttributeStringValuesByKey(document.getDocumentId(), "myMultiValuedAttribute");
142            assertEquals(2, attributeValues.size());
143            assertTrue(attributeValues.contains("value1"));
144            assertTrue(attributeValues.contains("value2"));
145    
146            DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
147            DocumentSearchResults results = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(ewestfal, builder.build());
148            assertEquals(1, results.getSearchResults().size());
149            DocumentSearchResult result = results.getSearchResults().get(0);
150    
151            // TODO - the below assertions really should work, but they currently don't.  Currently, unless you pass the
152            // specific document type as one of the criteria the document attributes are not returned with the search
153            // There should at least be an option on the search api to enable the returning of document attributes from
154            // the search API - see https://jira.kuali.org/browse/KULRICE-6764
155            /*assertEquals(1, result.getDocumentAttributes().size());
156            DocumentAttribute attribute = result.getDocumentAttributes().get(0);
157            assertEquals("myAttribute", attribute.getName());
158            assertEquals("myValue", attribute.getValue());
159            assertEquals(DocumentAttributeDataType.STRING, attribute.getDataType());*/
160    
161            // now do a document search targeting the specific customizer document type, the result should be customized
162            // and the "myAttribute" attribute should have a customized value of "myCustomizedValue", also the
163            // "myMultiValuedAttribute" should now only have a single value of "value0"
164            
165            builder.setDocumentTypeName("DocumentSearchCustomizerTest");
166            results = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(ewestfal, builder.build());
167            assertEquals(1, results.getSearchResults().size());
168            result = results.getSearchResults().get(0);
169            assertEquals(3, result.getDocumentAttributes().size());
170            for (DocumentAttribute attribute : result.getDocumentAttributes()) {
171                if (attribute.getName().equals("myAttribute")) {
172                    assertEquals("myAttribute", attribute.getName());
173                    assertEquals("myCustomizedValue", attribute.getValue());
174                    assertEquals(DocumentAttributeDataType.STRING, attribute.getDataType());
175                } else if (attribute.getName().equals("myMultiValuedAttribute")) {
176                    assertEquals("myMultiValuedAttribute", attribute.getName());
177                    assertEquals("value0", attribute.getValue());
178                    assertEquals(DocumentAttributeDataType.STRING, attribute.getDataType());
179                } else if (attribute.getName().equals("criteriaUserId")) {
180                    assertEquals("criteriaUserId", attribute.getName());
181                    assertEquals(ewestfal, attribute.getValue());
182                }else {
183                    fail("Encountered an attribute name which i didn't understand: " + attribute.getName());
184                }
185            }
186        }
187    
188        /**
189         * An implementation of a DocumentSearchCustomizer which does some simple customizations to allow us to test that
190         * the customizer is functioning properly.
191         */
192        public static final class Customizer extends DocumentSearchCustomizerBase {
193    
194            @Override
195            public DocumentSearchCriteria customizeCriteria(DocumentSearchCriteria documentSearchCriteria) {
196                DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create(documentSearchCriteria);
197                builder.setDocumentStatuses(Collections.singletonList(DocumentStatus.FINAL));
198                return builder.build();
199            }
200            @Override
201            public boolean isCustomizeCriteriaEnabled(String documentTypeName) {
202                return true;
203            }
204    
205            @Override
206            public DocumentSearchCriteria customizeClearCriteria(DocumentSearchCriteria documentSearchCriteria) {
207                // preserver applicationDocumentId on clear, but clear out everything else
208                DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
209                builder.setApplicationDocumentId(documentSearchCriteria.getApplicationDocumentId());
210                return builder.build();
211            }
212            @Override
213            public boolean isCustomizeClearCriteriaEnabled(String documentTypeName) {
214                return true;
215            }
216    
217            @Override
218            public DocumentSearchResultValues customizeResults(DocumentSearchCriteria documentSearchCriteria,
219                    List<DocumentSearchResult> defaultResults) {
220                if (defaultResults.size() == 1) {
221                    assertEquals(1, defaultResults.size());
222                    DocumentSearchResultValues.Builder valuesBuilder = DocumentSearchResultValues.Builder.create();
223    
224                    DocumentSearchResultValue.Builder resultValueBuilder = DocumentSearchResultValue.Builder.create(defaultResults.get(0).getDocument().getDocumentId());
225                    resultValueBuilder.getDocumentAttributes().add(DocumentAttributeFactory.loadContractIntoBuilder(DocumentAttributeFactory.createStringAttribute("myAttribute", "myCustomizedValue")));
226                    resultValueBuilder.getDocumentAttributes().add(DocumentAttributeFactory.loadContractIntoBuilder(DocumentAttributeFactory.createStringAttribute("myMultiValuedAttribute", "value0")));
227    
228                    // Return if principal id was foudn in criteria
229                    if(StringUtils.isNotBlank(documentSearchCriteria.getDocSearchUserId())) {
230                         resultValueBuilder.getDocumentAttributes().add(DocumentAttributeFactory.loadContractIntoBuilder(DocumentAttributeFactory.createStringAttribute("criteriaUserId", documentSearchCriteria.getDocSearchUserId())));
231                    }
232    
233                    valuesBuilder.getResultValues().add(resultValueBuilder);
234                    return valuesBuilder.build();
235                } else {
236                    return null;
237                }
238            }
239            @Override
240            public boolean isCustomizeResultsEnabled(String documentTypeName) {
241                return true;
242            }
243            
244        }
245    
246        public static final class CustomSearchAttribute implements SearchableAttribute {
247            @Override
248            public String generateSearchContent(ExtensionDefinition extensionDefinition,
249                    String documentTypeName,
250                    WorkflowAttributeDefinition attributeDefinition) {
251                return null;
252            }
253            @Override
254            public List<DocumentAttribute> extractDocumentAttributes(ExtensionDefinition extensionDefinition,
255                    DocumentWithContent documentWithContent) {
256                List<DocumentAttribute> attributes = new ArrayList<DocumentAttribute>();
257                attributes.add(DocumentAttributeFactory.createStringAttribute("myAttribute", "myValue"));
258                attributes.add(DocumentAttributeFactory.createStringAttribute("myMultiValuedAttribute", "value1"));
259                attributes.add(DocumentAttributeFactory.createStringAttribute("myMultiValuedAttribute", "value2"));
260                attributes.add(DocumentAttributeFactory.createStringAttribute("criteriaUserId", "blank"));
261                return attributes;
262            }
263            @Override
264            public List<RemotableAttributeField> getSearchFields(ExtensionDefinition extensionDefinition,
265                    String documentTypeName) {
266                List<RemotableAttributeField> searchFields = new ArrayList<RemotableAttributeField>();
267                RemotableAttributeField.Builder builder = RemotableAttributeField.Builder.create("myAttribute");
268                searchFields.add(builder.build());
269                builder = RemotableAttributeField.Builder.create("myMultiValuedAttribute");
270                searchFields.add(builder.build());
271                builder = RemotableAttributeField.Builder.create("criteriaUserId");
272                searchFields.add(builder.build());
273                return searchFields;
274            }
275            @Override
276            public List<RemotableAttributeError> validateDocumentAttributeCriteria(ExtensionDefinition extensionDefinition,
277                    DocumentSearchCriteria documentSearchCriteria) {
278                return null;
279            }
280        }
281    
282    }