001/**
002 * Copyright 2005-2014 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 */
016package org.kuali.rice.kew.docsearch;
017
018import org.apache.commons.lang.StringUtils;
019import org.junit.Test;
020import org.kuali.rice.core.api.uif.RemotableAttributeError;
021import org.kuali.rice.core.api.uif.RemotableAttributeField;
022import org.kuali.rice.kew.api.KewApiServiceLocator;
023import org.kuali.rice.kew.api.WorkflowDocument;
024import org.kuali.rice.kew.api.WorkflowDocumentFactory;
025import org.kuali.rice.kew.api.document.DocumentStatus;
026import org.kuali.rice.kew.api.document.DocumentWithContent;
027import org.kuali.rice.kew.api.document.attribute.DocumentAttribute;
028import org.kuali.rice.kew.api.document.attribute.DocumentAttributeDataType;
029import org.kuali.rice.kew.api.document.attribute.DocumentAttributeFactory;
030import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition;
031import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
032import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
033import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
034import org.kuali.rice.kew.api.extension.ExtensionDefinition;
035import org.kuali.rice.kew.doctype.bo.DocumentType;
036import org.kuali.rice.kew.framework.document.attribute.SearchableAttribute;
037import org.kuali.rice.kew.framework.document.search.DocumentSearchCustomizerBase;
038import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValue;
039import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValues;
040import org.kuali.rice.kew.service.KEWServiceLocator;
041import org.kuali.rice.kew.test.KEWTestCase;
042
043import java.util.ArrayList;
044import java.util.Collections;
045import java.util.List;
046
047import 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 */
055public 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}