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