From 043a158c8023fe31c23afe721f3760196f2810ba Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Tue, 6 Sep 2016 22:03:09 +0000 Subject: [PATCH] Merged 5.2.N (5.2.1) to HEAD (5.2) 130169 gjames: Merged searchapi (5.2.1) to 5.2.N (5.2.1) 129778 gjames: SEARCH-113: Initial implementation of the Search public API git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@130321 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/public-rest-context.xml | 8 + .../api/SearchApiWebscript.post.desc.xml | 10 ++ .../rest/api/search/SearchApiWebscript.java | 130 +++++++++++++++ .../rest/api/search/impl/ResultMapper.java | 91 +++++++++++ .../rest/api/search/impl/SearchMapper.java | 102 ++++++++++++ .../alfresco/rest/api/search/model/Query.java | 72 +++++++++ .../rest/api/search/model/SearchQuery.java | 51 ++++++ .../alfresco/remoteapi/AllUnitTestsSuite.java | 2 + .../rest/api/search/AllSearchApiTests.java | 41 +++++ .../search/BasicSearchApiIntegrationTest.java | 79 +++++++++ .../rest/api/search/ResultMapperTests.java | 150 ++++++++++++++++++ .../rest/api/search/SearchMapperTests.java | 139 ++++++++++++++++ .../search/SearchQuerySerializerTests.java | 83 ++++++++++ .../org/alfresco/rest/api/tests/ApiTest.java | 2 + 14 files changed, 960 insertions(+) create mode 100644 config/alfresco/templates/publicapi/org/alfresco/api/SearchApiWebscript.post.desc.xml create mode 100644 source/java/org/alfresco/rest/api/search/SearchApiWebscript.java create mode 100644 source/java/org/alfresco/rest/api/search/impl/ResultMapper.java create mode 100644 source/java/org/alfresco/rest/api/search/impl/SearchMapper.java create mode 100644 source/java/org/alfresco/rest/api/search/model/Query.java create mode 100644 source/java/org/alfresco/rest/api/search/model/SearchQuery.java create mode 100644 source/test-java/org/alfresco/rest/api/search/AllSearchApiTests.java create mode 100644 source/test-java/org/alfresco/rest/api/search/BasicSearchApiIntegrationTest.java create mode 100644 source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java create mode 100644 source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java create mode 100644 source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index c220b852b6..bc21b1d21b 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -853,6 +853,14 @@ + + + + + + + diff --git a/config/alfresco/templates/publicapi/org/alfresco/api/SearchApiWebscript.post.desc.xml b/config/alfresco/templates/publicapi/org/alfresco/api/SearchApiWebscript.post.desc.xml new file mode 100644 index 0000000000..ac2d22de84 --- /dev/null +++ b/config/alfresco/templates/publicapi/org/alfresco/api/SearchApiWebscript.post.desc.xml @@ -0,0 +1,10 @@ + + + Handles POST for the Search api + Hands back a little bit of JSON + /public/search/versions/1/search + user + required + argument + public_api + \ No newline at end of file diff --git a/source/java/org/alfresco/rest/api/search/SearchApiWebscript.java b/source/java/org/alfresco/rest/api/search/SearchApiWebscript.java new file mode 100644 index 0000000000..f7ecdccf88 --- /dev/null +++ b/source/java/org/alfresco/rest/api/search/SearchApiWebscript.java @@ -0,0 +1,130 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.search; + +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.Node; +import org.alfresco.rest.api.search.impl.ResultMapper; +import org.alfresco.rest.api.search.impl.SearchMapper; +import org.alfresco.rest.api.search.model.SearchQuery; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Params; +import org.alfresco.rest.framework.tools.ApiAssistant; +import org.alfresco.rest.framework.tools.RecognizedParamsExtractor; +import org.alfresco.rest.framework.tools.RequestReader; +import org.alfresco.rest.framework.tools.ResponseWriter; +import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper; +import org.alfresco.rest.framework.webscripts.WithResponse; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.util.ParameterCheck; +import org.alfresco.util.PropertyCheck; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.webscripts.AbstractWebScript; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + +import java.io.IOException; + +/** + * An implementation of the {{baseUrl}}/{{networkId}}/public/search/versions/1/search endpoint + * + * @author Gethin James + */ +public class SearchApiWebscript extends AbstractWebScript implements RecognizedParamsExtractor, RequestReader, ResponseWriter, + InitializingBean +{ + private ServiceRegistry serviceRegistry; + private SearchService searchService; + private Nodes nodes; + private SearchMapper searchMapper; + private ResultMapper resultMapper; + protected ApiAssistant assistant; + protected ResourceWebScriptHelper helper; + + @Override + public void afterPropertiesSet() + { + PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry); + this.searchService = serviceRegistry.getSearchService(); + ParameterCheck.mandatory("assistant", this.assistant); + ParameterCheck.mandatory("nodes", this.nodes); + + searchMapper = new SearchMapper(); + resultMapper = new ResultMapper(nodes); + } + + @Override + public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException + { + + try { + //Turn JSON into a Java object respresentation + SearchQuery searchQuery = extractJsonContent(webScriptRequest, assistant.getJsonHelper(), SearchQuery.class); + + //Parse the parameter + Params.RecognizedParams recognizedParams = new Params.RecognizedParams(null, null, null, null, null, null, null, null, false); + Params params = Params.valueOf(null, recognizedParams, searchQuery, webScriptRequest); + + //Turn the params into the Java SearchParameters object + SearchParameters searchParams = searchMapper.toSearchParameters(params); + + //Call searchService + ResultSet results = searchService.query(searchParams); + + //Turn solr results into JSON + CollectionWithPagingInfo resultJson = resultMapper.toCollectionWithPagingInfo(params, results); + //Post-process the request and pass in params, eg. params.getFilter() + Object toRender = helper.processAdditionsToTheResponse(null, null, null, params, resultJson); + + //Write response + setResponse(webScriptResponse, DEFAULT_SUCCESS); + renderJsonResponse(webScriptResponse, toRender, assistant.getJsonHelper()); + + } catch (Exception exception) { + renderException(exception,webScriptResponse,assistant); + } + } + + public void setNodes(Nodes nodes) { + this.nodes = nodes; + } + + public void setAssistant(ApiAssistant assistant) { + this.assistant = assistant; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } + + public void setHelper(ResourceWebScriptHelper helper) + { + this.helper = helper; + } +} diff --git a/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java b/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java new file mode 100644 index 0000000000..615b3db7f3 --- /dev/null +++ b/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java @@ -0,0 +1,91 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.search.impl; + +import org.alfresco.model.ContentModel; +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.Node; +import org.alfresco.rest.api.model.UserInfo; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Paging; +import org.alfresco.rest.framework.resource.parameters.Params; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.lang.NotImplementedException; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_ASPECTNAMES; +import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_PROPERTIES; + +/** + * Maps from a Solr ResultSet to a json public api representation. + * + * @author Gethin James + */ +public class ResultMapper +{ + + private Nodes nodes; + + public ResultMapper(Nodes nodes) + { + this.nodes = nodes; + ParameterCheck.mandatory("nodes", this.nodes); + } + + /** + * + * @param params + * @param results + * @return + */ + public CollectionWithPagingInfo toCollectionWithPagingInfo(Params params, ResultSet results) + { + Long totalItems = results.getNumberFound(); + List noderesults = new ArrayList(); + + results.forEach(row -> + { + Node aNode = nodes.getFolderOrDocument(row.getNodeRef(), null, null, params.getInclude(), null); + //float f = row.getScore(); + //Long dbId = (Long) row.getValue(ContentModel.PROP_NODE_DBID); + noderesults.add(aNode); + } + ); + + Integer total = Integer.valueOf(totalItems.intValue()); + return CollectionWithPagingInfo.asPaged(null, noderesults, noderesults.size() < total, total); + } + +} diff --git a/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java b/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java new file mode 100644 index 0000000000..e4761f9483 --- /dev/null +++ b/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java @@ -0,0 +1,102 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.search.impl; + +import org.alfresco.rest.api.search.model.Query; +import org.alfresco.rest.api.search.model.SearchQuery; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.rest.framework.resource.content.BasicContentInfo; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Params; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.rest.api.model.Node; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.lang.NotImplementedException; + +import static org.alfresco.service.cmr.search.SearchService.*; + +/** + * Maps from a json request and a solr SearchParameters object. + * + * @author Gethin James + */ +public class SearchMapper +{ + + /** + * Turn the params into the Java SearchParameters object + * @param params + * @return + */ + public SearchParameters toSearchParameters(Params params) + { + BasicContentInfo info = params.getContentInfo(); + SearchQuery searchQuery = (SearchQuery) params.getPassedIn(); + + ParameterCheck.mandatory("query", searchQuery.getQuery()); + + SearchParameters sp = new SearchParameters(); + fromQuery(sp, searchQuery.getQuery()); + + //Hardcode workspace store + sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + return sp; + } + + /** + * + * @param sp + * @param q + */ + public void fromQuery(SearchParameters sp, Query q) + { + ParameterCheck.mandatoryString("query", q.getQuery()); + ParameterCheck.mandatoryString("language", q.getLanguage()); + + switch (q.getLanguage().toLowerCase()) + { + case "afts": + sp.setLanguage(LANGUAGE_FTS_ALFRESCO); + break; + case "lucene": + sp.setLanguage(LANGUAGE_LUCENE); + break; + case "cmis": + sp.setLanguage(LANGUAGE_CMIS_ALFRESCO); + break; + default: + throw new InvalidArgumentException(InvalidArgumentException.DEFAULT_MESSAGE_ID, + new Object[] { ": language allowed values: afts,lucene,cmis" }); + } + sp.setQuery(q.getQuery()); + sp.setSearchTerm(q.getUserQuery()); + + } +} diff --git a/source/java/org/alfresco/rest/api/search/model/Query.java b/source/java/org/alfresco/rest/api/search/model/Query.java new file mode 100644 index 0000000000..6699164ac0 --- /dev/null +++ b/source/java/org/alfresco/rest/api/search/model/Query.java @@ -0,0 +1,72 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.search.model; + +/** + * POJO class representing the query element of the JSON body + **/ +public class Query +{ + + String language; + String query; + String userQuery; + + public Query() + { + } + + public String getLanguage() + { + return language; + } + + public void setLanguage(String language) + { + this.language = language; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public String getUserQuery() + { + return userQuery; + } + + public void setUserQuery(String userQuery) + { + this.userQuery = userQuery; + } +} diff --git a/source/java/org/alfresco/rest/api/search/model/SearchQuery.java b/source/java/org/alfresco/rest/api/search/model/SearchQuery.java new file mode 100644 index 0000000000..eae6984512 --- /dev/null +++ b/source/java/org/alfresco/rest/api/search/model/SearchQuery.java @@ -0,0 +1,51 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.search.model; + +/** + * POJO class representing the JSON body for a search request + * + * @author Gethin James + */ +public class SearchQuery +{ + Query query; + + public SearchQuery() + { + } + + public Query getQuery() + { + return query; + } + + public void setQuery(Query query) + { + this.query = query; + } +} diff --git a/source/test-java/org/alfresco/remoteapi/AllUnitTestsSuite.java b/source/test-java/org/alfresco/remoteapi/AllUnitTestsSuite.java index 08522b6e94..8c024a37c3 100644 --- a/source/test-java/org/alfresco/remoteapi/AllUnitTestsSuite.java +++ b/source/test-java/org/alfresco/remoteapi/AllUnitTestsSuite.java @@ -37,6 +37,7 @@ import org.alfresco.repo.webdav.LockInfoImplTest; import org.alfresco.repo.webdav.RenameShuffleDetectionTest; import org.alfresco.repo.webdav.WebDAVHelperTest; import org.alfresco.repo.webdav.WebDAVLockServiceImplTest; +import org.alfresco.rest.api.search.AllSearchApiTests; import org.alfresco.rest.api.tests.ModulePackageTest; import org.alfresco.rest.framework.tests.core.AllRestFrameworkTest; import org.alfresco.rest.framework.tests.metadata.WriterTests; @@ -66,6 +67,7 @@ public class AllUnitTestsSuite extends TestSuite static void publicApiTests(TestSuite suite) { suite.addTest(new JUnit4TestAdapter(AllRestFrameworkTest.class)); + suite.addTest(new JUnit4TestAdapter(AllSearchApiTests.class)); suite.addTest(new JUnit4TestAdapter(WriterTests.class)); suite.addTest(new JUnit4TestAdapter(ModulePackageTest.class)); } diff --git a/source/test-java/org/alfresco/rest/api/search/AllSearchApiTests.java b/source/test-java/org/alfresco/rest/api/search/AllSearchApiTests.java new file mode 100644 index 0000000000..4443a85208 --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/search/AllSearchApiTests.java @@ -0,0 +1,41 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.search; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +/** + * The suite of tests for the Search Api + * + * @author Gethin James + */ +@RunWith(Suite.class) +@SuiteClasses({ SearchMapperTests.class, ResultMapperTests.class,SearchQuerySerializerTests.class}) +public class AllSearchApiTests +{ +} diff --git a/source/test-java/org/alfresco/rest/api/search/BasicSearchApiIntegrationTest.java b/source/test-java/org/alfresco/rest/api/search/BasicSearchApiIntegrationTest.java new file mode 100644 index 0000000000..cf130d169d --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/search/BasicSearchApiIntegrationTest.java @@ -0,0 +1,79 @@ +package org.alfresco.rest.api.search; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import org.alfresco.rest.AbstractSingleNetworkSiteTest; +import org.alfresco.rest.api.Queries; +import org.alfresco.rest.api.tests.client.HttpResponse; +import org.alfresco.rest.api.tests.client.PublicApiClient.ExpectedPaging; +import org.alfresco.rest.api.tests.client.PublicApiClient.Paging; +import org.alfresco.rest.api.tests.client.data.FolderNode; +import org.alfresco.rest.api.tests.client.data.Node; +import org.alfresco.rest.api.tests.util.RestApiUtil; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Basic test to exercise the Search API endpoint + * + *

POST:

+ * {@literal :/alfresco/api//public/search/versions/1/search} + * + * @author Gethin James + */ +public class BasicSearchApiIntegrationTest extends AbstractSingleNetworkSiteTest +{ + private static final String URL_SEARCH = "search"; + private static final String SEARCH_API_NAME = "search"; + private static final String json = "{ \"query\": {\"query\": \"cm:name:king\",\"userQuery\": \"great\",\"language\": \"afts\"}}"; + private static final String bad_json = "{ \"query\": {\"qu\": \"cm:some nonsense \",\"userQuery\": \"great\",\"language\": \"afts\"}}"; + /** + * Tests basic api for search + */ + @Test + public void testQuery() throws Exception + { + setRequestContext(user1); + String f1Id = null; + try + { + // As user 1 ... + // Try to get nodes with search term 'king*' - assume clean repo (ie. none to start with) + HttpResponse response = post(URL_SEARCH, json, null, null, SEARCH_API_NAME, 200); + List nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + String myFolderNodeId = getMyNodeId(); + + f1Id = createFolder(myFolderNodeId, "king").getId(); + + response = post(URL_SEARCH, json, null, null, SEARCH_API_NAME, 200); + ExpectedPaging paging = RestApiUtil.parsePaging(response.getJsonResponse()); + assertEquals(1, paging.getTotalItems().intValue()); + assertFalse(paging.getHasMoreItems()); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(1, nodes.size()); + + } + finally + { + // some cleanup + if (f1Id != null) + { + deleteNode(f1Id, true, 204); + } + } + } + + @Test + public void testBadQuery() throws Exception + { + setRequestContext(user1); + //Bad request + HttpResponse response = post(URL_SEARCH, bad_json, null, null, SEARCH_API_NAME, 400); + } +} diff --git a/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java b/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java new file mode 100644 index 0000000000..b99062864b --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java @@ -0,0 +1,150 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.search; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.notNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.alfresco.repo.search.EmptyResultSet; +import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet; +import org.alfresco.repo.search.results.ChildAssocRefResultSet; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.impl.NodesImpl; +import org.alfresco.rest.api.model.Node; +import org.alfresco.rest.api.model.UserInfo; +import org.alfresco.rest.api.search.impl.ResultMapper; +import org.alfresco.rest.api.search.model.SearchQuery; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Params; +import org.alfresco.rest.framework.tests.core.JsonJacksonTests; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.LimitBy; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.extensions.surf.util.Content; +import org.springframework.extensions.webscripts.Match; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.http.HttpMethod; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Tests the ResultMapper class + * + * @author Gethin James + */ +public class ResultMapperTests +{ + static ResultMapper mapper; + public static final String JSON_REPONSE = "{\"responseHeader\":{\"status\":0,\"QTime\":9},\"_original_parameters_\":\"org.apache.solr.common.params.DefaultSolrParams:{params(df=TEXT&alternativeDic=DEFAULT_DICTIONARY&fl=DBID,score&start=0&fq={!afts}AUTHORITY_FILTER_FROM_JSON&fq={!afts}TENANT_FILTER_FROM_JSON&rows=1000&locale=en_US&wt=json),defaults(carrot.url=id&spellcheck.collateExtendedResults=true&carrot.produceSummary=true&spellcheck.maxCollations=3&spellcheck.maxCollationTries=5&spellcheck.alternativeTermCount=2&spellcheck.extendedResults=false&defType=afts&spellcheck.maxResultsForSuggest=5&spellcheck=false&carrot.outputSubClusters=false&spellcheck.count=5&carrot.title=mltext@m___t@{http://www.alfresco.org/model/content/1.0}title&carrot.snippet=content@s___t@{http://www.alfresco.org/model/content/1.0}content&spellcheck.collate=true)}\",\"_field_mappings_\":{},\"_date_mappings_\":{},\"_range_mappings_\":{},\"_pivot_mappings_\":{},\"_interval_mappings_\":{},\"_stats_field_mappings_\":{},\"_stats_facet_mappings_\":{},\"_facet_function_mappings_\":{},\"response\":{\"numFound\":6,\"start\":0,\"maxScore\":0.7849362,\"docs\":[{\"DBID\":565,\"score\":0.7849362},{\"DBID\":566,\"score\":0.7849362},{\"DBID\":521,\"score\":0.3540957},{\"DBID\":514,\"score\":0.33025497},{\"DBID\":420,\"score\":0.32440513},{\"DBID\":415,\"score\":0.2780319}]},\"processedDenies\":true}"; + + @BeforeClass + public static void setupTests() throws Exception + { + Map mapUserInfo = new HashMap<>(); + mapUserInfo.put(AuthenticationUtil.getSystemUserName(), new UserInfo(AuthenticationUtil.getSystemUserName(), "sys", "sys")); + Map nodeProps = new HashMap<>(); + + NodesImpl nodes = mock(NodesImpl.class); + ServiceRegistry sr = mock(ServiceRegistry.class); + nodes.setServiceRegistry(sr); + + when(nodes.getFolderOrDocument(notNull(NodeRef.class), any(), any(), notNull(List.class), any())).thenAnswer(new Answer() { + @Override + public Node answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + return new Node((NodeRef)args[0], (NodeRef)args[1], nodeProps, mapUserInfo, sr); + } + }); + mapper = new ResultMapper(nodes); + } + + @Test + public void testNoResults() throws Exception + { + CollectionWithPagingInfo collection = mapper.toCollectionWithPagingInfo(mockParams(),new EmptyResultSet()); + assertNotNull(collection); + assertFalse(collection.hasMoreItems()); + assertTrue(collection.getTotalItems() < 1); + } + + @Test + public void testToCollectionWithPagingInfo() throws Exception + { + ResultSet results = mockResultset(); + CollectionWithPagingInfo collection = mapper.toCollectionWithPagingInfo(mockParams(),results); + assertNotNull(collection); + Long found = results.getNumberFound(); + assertEquals(found.intValue(), collection.getTotalItems().intValue()); + } + + private ResultSet mockResultset() throws JSONException + { + + NodeService nodeService = mock(NodeService.class); + when(nodeService.getNodeRef(any())).thenReturn(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, GUID.generate())); + + SearchParameters sp = new SearchParameters(); + sp.setBulkFetchEnabled(false); + JSONObject json = new JSONObject(new JSONTokener(JSON_REPONSE)); + ResultSet results = new SolrJSONResultSet(json,sp,nodeService, null, LimitBy.FINAL_SIZE, 10); + return results; + } + + private Params mockParams() + { + Params params = mock(Params.class); + when(params.getInclude()).thenReturn(new ArrayList()); + return params; + } + +} diff --git a/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java b/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java new file mode 100644 index 0000000000..e19ed0f8ee --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java @@ -0,0 +1,139 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.search; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.fail; +import static org.alfresco.service.cmr.search.SearchService.LANGUAGE_CMIS_ALFRESCO; +import static org.alfresco.service.cmr.search.SearchService.LANGUAGE_FTS_ALFRESCO; +import static org.alfresco.service.cmr.search.SearchService.LANGUAGE_LUCENE; +import org.alfresco.rest.api.search.impl.SearchMapper; +import org.alfresco.rest.api.search.model.Query; +import org.alfresco.rest.api.search.model.SearchQuery; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.rest.framework.resource.parameters.Params; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchParameters; +import org.junit.Test; + +/** + * Tests the SearchMapper class + * + * @author Gethin James + */ +public class SearchMapperTests +{ + + static SearchMapper searchMapper = new SearchMapper(); + + @Test(expected = IllegalArgumentException.class) + public void testMandatory() throws Exception + { + Params params = Params.valueOf(null, null, new SearchQuery(), null); + SearchParameters searchParameters = searchMapper.toSearchParameters(params); + } + + @Test + public void toSearchParameters() throws Exception + { + + Params params = Params.valueOf(null, null, minimalQuery(), null); + SearchParameters searchParameters = searchMapper.toSearchParameters(params); + assertNotNull(searchParameters); + + //Test defaults + assertEquals("There should be only 1 default store", 1,searchParameters.getStores().size()); + assertEquals("workspaces store is the default", StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, searchParameters.getStores().get(0)); + } + + @Test + public void fromQuery() throws Exception + { + SearchParameters searchParameters = new SearchParameters(); + try + { + searchMapper.fromQuery(searchParameters, new Query()); + fail(); + } catch (IllegalArgumentException iae) + { + assertTrue(iae.getLocalizedMessage().contains("query is a mandatory parameter")); + } + + Query q = new Query(); + q.setQuery("hello"); + + try + { + searchMapper.fromQuery(searchParameters, q); + fail(); + } catch (IllegalArgumentException iae) + { + assertTrue(iae.getLocalizedMessage().contains("language is a mandatory parameter")); + } + + q.setLanguage("world"); + + try + { + searchMapper.fromQuery(searchParameters, q); + fail(); + } catch (InvalidArgumentException iae) + { + assertNotNull(iae); + //world is not a valid language type + } + + q.setLanguage("afts"); + searchMapper.fromQuery(searchParameters, q); + assertEquals(LANGUAGE_FTS_ALFRESCO, searchParameters.getLanguage()); + + q.setLanguage("cMiS"); + searchMapper.fromQuery(searchParameters, q); + assertEquals(LANGUAGE_CMIS_ALFRESCO, searchParameters.getLanguage()); + + q.setLanguage("LuCENE"); + searchMapper.fromQuery(searchParameters, q); + assertEquals(LANGUAGE_LUCENE, searchParameters.getLanguage()); + + assertEquals("hello", searchParameters.getQuery()); + + q.setUserQuery("Heload"); + searchMapper.fromQuery(searchParameters, q); + assertEquals("Heload", searchParameters.getSearchTerm()); + } + + private SearchQuery minimalQuery() + { + SearchQuery sq = new SearchQuery(); + Query query = new Query(); + sq.setQuery(query); + query.setQuery("foo"); + query.setLanguage("cmis"); + return sq; + } +} diff --git a/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java b/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java new file mode 100644 index 0000000000..7d1c0c0603 --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java @@ -0,0 +1,83 @@ +/*- + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.search; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.alfresco.rest.api.model.Comment; +import org.alfresco.rest.api.search.model.SearchQuery; +import org.alfresco.rest.framework.jacksonextensions.JacksonHelper; +import org.alfresco.rest.framework.jacksonextensions.RestJsonModule; +import org.alfresco.rest.framework.tools.ApiAssistant; +import org.alfresco.rest.framework.tools.RequestReader; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.extensions.surf.util.Content; +import org.springframework.extensions.webscripts.WebScriptRequest; + +import java.io.IOException; +import java.io.StringReader; + +/** + * Tests json -> SearchQuery deserialization + * + * @author Gethin James + */ +public class SearchQuerySerializerTests implements RequestReader +{ + static JacksonHelper jsonHelper = null; + + @BeforeClass + public static void setupTests() throws Exception + { + jsonHelper = new JacksonHelper(); + RestJsonModule module = new RestJsonModule(); + jsonHelper.setModule(module); + jsonHelper.afterPropertiesSet(); + } + + @Test + public void testDeserializeQuery() throws IOException + { + String json = "{ \"query\": {\"query\": \"g*\",\"userQuery\": \"great\",\"language\": \"bob\"}}"; + SearchQuery searchQuery = extractFromJson(json); + assertEquals(SearchQuery.class, searchQuery.getClass()); + assertEquals("bob", searchQuery.getQuery().getLanguage()); + assertEquals("g*", searchQuery.getQuery().getQuery()); + assertEquals("great", searchQuery.getQuery().getUserQuery()); + } + + private SearchQuery extractFromJson(String json) throws IOException + { + Content content = mock(Content.class); + when(content.getReader()).thenReturn(new StringReader(json)); + WebScriptRequest request = mock(WebScriptRequest.class); + when(request.getContent()).thenReturn(content); + return extractJsonContent(request, jsonHelper, SearchQuery.class); + } + +} diff --git a/source/test-java/org/alfresco/rest/api/tests/ApiTest.java b/source/test-java/org/alfresco/rest/api/tests/ApiTest.java index c5e0258822..d9ee7fb745 100644 --- a/source/test-java/org/alfresco/rest/api/tests/ApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/ApiTest.java @@ -26,6 +26,7 @@ package org.alfresco.rest.api.tests; import org.alfresco.rest.DeletedNodesTest; +import org.alfresco.rest.api.search.BasicSearchApiIntegrationTest; import org.junit.AfterClass; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -45,6 +46,7 @@ import org.junit.runners.Suite; NodeApiTest.class, NodeAssociationsApiTest.class, NodeVersionsApiTest.class, + BasicSearchApiIntegrationTest.class, QueriesNodesApiTest.class, QueriesPeopleApiTest.class, QueriesSitesApiTest.class,