diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index acaff09cd2..97550882c6 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -839,7 +839,7 @@ - + @@ -894,13 +894,22 @@ + + + + + + + + + + - - - + + listDeleted(Parameters parameters); - Node getDeletedNode(String originalId, Parameters parameters); + + /** + * Gets a single deleted node by id. + * @param originalId + * @param parameters + * @param fullnode Should we return the full representation of the minimal one? + * @param mapUserInfo + * @return a deleted node + */ + Node getDeletedNode(String originalId, Parameters parameters, boolean fullnode, Map mapUserInfo); + + /** + * Restores a deleted node and returns it. + * @param archivedId + * @return the new undeleted node. + */ Node restoreArchivedNode(String archivedId); + + /** + * Permanently delete the node. + * @param archivedId + */ void purgeArchivedNode(String archivedId); } diff --git a/source/java/org/alfresco/rest/api/impl/DeletedNodesImpl.java b/source/java/org/alfresco/rest/api/impl/DeletedNodesImpl.java index c94e823a66..542691c6c2 100644 --- a/source/java/org/alfresco/rest/api/impl/DeletedNodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/DeletedNodesImpl.java @@ -130,7 +130,7 @@ public class DeletedNodesImpl implements DeletedNodes } @Override - public Node getDeletedNode(String originalId, Parameters parameters) + public Node getDeletedNode(String originalId, Parameters parameters, boolean fullnode, Map mapUserInfo) { //First check the node is valid and has been archived. NodeRef validatedNodeRef = nodes.validateNode(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, originalId); @@ -139,8 +139,16 @@ public class DeletedNodesImpl implements DeletedNodes NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, validatedNodeRef.getId()); NodeRef archivedNodeRef = nodeArchiveService.getArchivedNode(nodeRef); - //Turn it into a JSON Node (FULL version) - Node foundNode = nodes.getFolderOrDocumentFullInfo(archivedNodeRef, null, null, parameters, null); + Node foundNode = null; + if (fullnode) + { + foundNode = nodes.getFolderOrDocumentFullInfo(archivedNodeRef, null, null, parameters, mapUserInfo); + } + else + { + foundNode = nodes.getFolderOrDocument(archivedNodeRef, null, null, parameters.getInclude(), mapUserInfo); + } + if (foundNode != null) mapArchiveInfo(foundNode,null); return foundNode; } diff --git a/source/java/org/alfresco/rest/api/model/Node.java b/source/java/org/alfresco/rest/api/model/Node.java index f668225818..32993b2c3d 100644 --- a/source/java/org/alfresco/rest/api/model/Node.java +++ b/source/java/org/alfresco/rest/api/model/Node.java @@ -70,7 +70,9 @@ public class Node implements Comparable protected UserInfo archivedByUser; // Version info - specifically for version node - see Version History API + protected String versionLabel; protected String versionComment; + protected String nodeId; //This is the frozen node id NOT the current node id protected Boolean isFolder; protected Boolean isFile; @@ -95,6 +97,7 @@ public class Node implements Comparable //optional SearchEntry (only ever returned from a search) protected SearchEntry search = null; + protected String location; public Node(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, Map mapUserInfo, ServiceRegistry sr) { @@ -377,6 +380,16 @@ public class Node implements Comparable this.archivedByUser = archivedByUser; } + public String getVersionLabel() + { + return versionLabel; + } + + public void setVersionLabel(String versionLabel) + { + this.versionLabel = versionLabel; + } + public String getVersionComment() { return versionComment; @@ -387,6 +400,26 @@ public class Node implements Comparable this.versionComment = versionComment; } + public String getLocation() + { + return location; + } + + public void setLocation(String location) + { + this.location = location; + } + + public String getNodeId() + { + return nodeId; + } + + public void setNodeId(String nodeId) + { + this.nodeId = nodeId; + } + public boolean equals(Object other) { if(this == other) @@ -451,6 +484,22 @@ public class Node implements Comparable { sb.append(", archivedByUser=").append(getArchivedByUser()); } + if (getVersionLabel() != null) + { + sb.append(", versionLabel=").append(getVersionLabel()); + } + if (getVersionComment() != null) + { + sb.append(", versionComment=").append(getVersionComment()); + } + if (getLocation() != null) + { + sb.append(", location=").append(getLocation()); + } + if (getNodeId() != null) + { + sb.append(", nodeId=").append(getNodeId()); + } if (getIsLink() != null) { sb.append(", isLink=").append(getIsLink()); // note: symbolic link (not shared link) diff --git a/source/java/org/alfresco/rest/api/nodes/NodeVersionsRelation.java b/source/java/org/alfresco/rest/api/nodes/NodeVersionsRelation.java index 95b4fbca5f..dd5d1ac440 100644 --- a/source/java/org/alfresco/rest/api/nodes/NodeVersionsRelation.java +++ b/source/java/org/alfresco/rest/api/nodes/NodeVersionsRelation.java @@ -133,8 +133,12 @@ public class NodeVersionsRelation extends AbstractNodeRelation implements private void mapVersionInfo(Version v, Node aNode) { - aNode.setNodeRef(new NodeRef("", "", v.getVersionLabel())); - + mapVersionInfo(v, aNode, new NodeRef("", "", v.getVersionLabel())); + } + + public void mapVersionInfo(Version v, Node aNode, NodeRef nodeRef) + { + aNode.setNodeRef(nodeRef); aNode.setVersionComment(v.getDescription()); Map props = aNode.getProperties(); @@ -279,7 +283,7 @@ public class NodeVersionsRelation extends AbstractNodeRelation implements } } - private Version findVersion(String nodeId, String versionLabelId) + public Version findVersion(String nodeId, String versionLabelId) { NodeRef nodeRef = nodes.validateOrLookupNode(nodeId, null); VersionHistory vh = versionService.getVersionHistory(nodeRef); diff --git a/source/java/org/alfresco/rest/api/search/SearchApiWebscript.java b/source/java/org/alfresco/rest/api/search/SearchApiWebscript.java index 23b12c3b1e..0806e3d267 100644 --- a/source/java/org/alfresco/rest/api/search/SearchApiWebscript.java +++ b/source/java/org/alfresco/rest/api/search/SearchApiWebscript.java @@ -77,8 +77,8 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry); this.searchService = serviceRegistry.getSearchService(); ParameterCheck.mandatory("assistant", this.assistant); - - searchMapper = new SearchMapper(); + ParameterCheck.mandatory("searchMapper", this.searchMapper); + ParameterCheck.mandatory("resultMapper", this.resultMapper); } @Override @@ -142,6 +142,11 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP return Params.valueOf(null, recognizedParams, null, webScriptRequest); } + public void setSearchMapper(SearchMapper searchMapper) + { + this.searchMapper = searchMapper; + } + public void setResultMapper(ResultMapper resultMapper) { this.resultMapper = resultMapper; diff --git a/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java b/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java index 66164c5095..4220af37fb 100644 --- a/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java +++ b/source/java/org/alfresco/rest/api/search/impl/ResultMapper.java @@ -26,11 +26,18 @@ package org.alfresco.rest.api.search.impl; +import static org.alfresco.rest.api.search.impl.StoreMapper.DELETED; +import static org.alfresco.rest.api.search.impl.StoreMapper.LIVE_NODES; +import static org.alfresco.rest.api.search.impl.StoreMapper.VERSIONS; import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet; +import org.alfresco.repo.version.Version2Model; +import org.alfresco.repo.version.VersionBaseModel; +import org.alfresco.rest.api.DeletedNodes; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.lookups.PropertyLookupRegistry; import org.alfresco.rest.api.model.Node; import org.alfresco.rest.api.model.UserInfo; +import org.alfresco.rest.api.nodes.NodeVersionsRelation; import org.alfresco.rest.api.search.context.FacetFieldContext; import org.alfresco.rest.api.search.context.FacetFieldContext.Bucket; import org.alfresco.rest.api.search.context.FacetQueryContext; @@ -38,15 +45,24 @@ import org.alfresco.rest.api.search.context.SearchContext; import org.alfresco.rest.api.search.context.SpellCheckContext; import org.alfresco.rest.api.search.model.HighlightEntry; import org.alfresco.rest.api.search.model.SearchEntry; +import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Params; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; import org.alfresco.service.cmr.search.SpellCheckResult; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -60,15 +76,38 @@ import java.util.Map.Entry; */ public class ResultMapper { - + private ServiceRegistry serviceRegistry; private Nodes nodes; + private NodeVersionsRelation nodeVersions; private PropertyLookupRegistry propertyLookup; + private StoreMapper storeMapper; + private DeletedNodes deletedNodes; private static Log logger = LogFactory.getLog(ResultMapper.class); public ResultMapper() { } + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + public void setNodeVersions(NodeVersionsRelation nodeVersions) + { + this.nodeVersions = nodeVersions; + } + + public void setDeletedNodes(DeletedNodes deletedNodes) + { + this.deletedNodes = deletedNodes; + } + + public void setStoreMapper(StoreMapper storeMapper) + { + this.storeMapper = storeMapper; + } + public void setNodes(Nodes nodes) { this.nodes = nodes; @@ -92,10 +131,12 @@ public class ResultMapper List noderesults = new ArrayList(); Map mapUserInfo = new HashMap<>(10); Map>>> hightLighting = results.getHighlighting(); + int notFound = 0; - results.forEach(row -> + for (ResultSetRow row:results) { - Node aNode = nodes.getFolderOrDocument(row.getNodeRef(), null, null, params.getInclude(), mapUserInfo); + Node aNode = getNode(row, params, mapUserInfo); + if (aNode != null) { float f = row.getScore(); @@ -116,15 +157,16 @@ public class ResultMapper else { logger.debug("Unknown noderef returned from search results "+row.getNodeRef()); + notFound++; } - }); + } SolrJSONResultSet solrResultSet = findSolrResultSet(results); if (solrResultSet != null) { //We used Solr for this query - context = toSearchContext(solrResultSet); + context = toSearchContext(solrResultSet, notFound); total = setTotal(solrResultSet); } else @@ -141,6 +183,65 @@ public class ResultMapper return CollectionWithPagingInfo.asPaged(params.getPaging(), noderesults, results.hasMore(), total, null, context); } + /** + * Builds a node representation based on a ResultSetRow; + * @param aRow + * @param params + * @param mapUserInfo + * @return Node + */ + public Node getNode(ResultSetRow aRow, Params params, Map mapUserInfo) + { + String nodeStore = storeMapper.getStore(aRow.getNodeRef()); + Node aNode = null; + switch (nodeStore) + { + case LIVE_NODES: + aNode = nodes.getFolderOrDocument(aRow.getNodeRef(), null, null, params.getInclude(), mapUserInfo); + break; + case VERSIONS: + Map properties = serviceRegistry.getNodeService().getProperties(aRow.getNodeRef()); + NodeRef frozenNodeRef = ((NodeRef) properties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF)); + String versionLabelId = (String) properties.get(Version2Model.PROP_QNAME_VERSION_LABEL); + Version v = null; + try + { + v = nodeVersions.findVersion(frozenNodeRef.getId(),versionLabelId); + aNode = nodes.getFolderOrDocument(v.getFrozenStateNodeRef(), null, null, params.getInclude(), mapUserInfo); + } + catch (EntityNotFoundException|InvalidNodeRefException e) + { + //Solr says there is a node but we can't find it + logger.debug("Failed to find a versioned node with id of "+frozenNodeRef + + " this is probably because the original node has been deleted."); + } + + if (v != null && aNode != null) + { + nodeVersions.mapVersionInfo(v, aNode, aRow.getNodeRef()); + aNode.setNodeId(frozenNodeRef.getId()); + aNode.setVersionLabel(versionLabelId); + } + break; + case DELETED: + try + { + aNode = deletedNodes.getDeletedNode(aRow.getNodeRef().getId(), params, false, mapUserInfo); + } + catch (EntityNotFoundException enfe) + { + //Solr says there is a deleted node but we can't find it, we want the rest of the search to return so lets ignore it. + logger.debug("Failed to find a deleted node with id of "+aRow.getNodeRef().getId()); + } + break; + } + if (aNode != null) + { + aNode.setLocation(nodeStore); + } + return aNode; + } + /** * Sets the total number found. * @param results @@ -158,7 +259,7 @@ public class ResultMapper * @param SolrJSONResultSet * @return SearchContext */ - public SearchContext toSearchContext(SolrJSONResultSet solrResultSet) + public SearchContext toSearchContext(SolrJSONResultSet solrResultSet, int notFound) { SearchContext context = null; Map facetQueries = solrResultSet.getFacetQueries(); diff --git a/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java b/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java index 17b34c2f06..5b5093d940 100644 --- a/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java +++ b/source/java/org/alfresco/rest/api/search/impl/SearchMapper.java @@ -83,6 +83,7 @@ public class SearchMapper public static final String CMIS = "cmis"; public static final String LUCENE = "lucene"; public static final String AFTS = "afts"; + private StoreMapper storeMapper; /** * Turn the SearchQuery params serialized by Jackson into the Java SearchParameters object @@ -430,7 +431,7 @@ public class SearchMapper { if (scope != null) { - List stores = scope.getStores(); + List stores = scope.getLocations(); if (stores!= null && !stores.isEmpty()) { //First reset the stores then add them. @@ -439,7 +440,7 @@ public class SearchMapper { try { - sp.addStore(new StoreRef(aStore)); + sp.addStore(storeMapper.getStoreRef(aStore)); } catch (AlfrescoRuntimeException are) { @@ -485,4 +486,9 @@ public class SearchMapper } } } + + public void setStoreMapper(StoreMapper storeMapper) + { + this.storeMapper = storeMapper; + } } diff --git a/source/java/org/alfresco/rest/api/search/impl/StoreMapper.java b/source/java/org/alfresco/rest/api/search/impl/StoreMapper.java new file mode 100644 index 0000000000..df39e2bc0a --- /dev/null +++ b/source/java/org/alfresco/rest/api/search/impl/StoreMapper.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.repo.version.Version2Model; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Maps to and from a StoreRef for the json public api. + * + * @author Gethin James + */ +public class StoreMapper +{ + + public static final String LIVE_NODES = "nodes"; + public static final String VERSIONS = "versions"; + public static final String DELETED = "deleted-nodes"; + + private static Log logger = LogFactory.getLog(StoreMapper.class); + + public static final StoreRef STORE_REF_VERSION2_SPACESSTORE = new StoreRef("workspace", Version2Model.STORE_ID); + + /** + * Work out which StoreRef this store belongs to. + * @param String representing a store + * @return StoreRef + */ + public StoreRef getStoreRef(String store) + { + if (store != null && !store.isEmpty()) + { + switch (store.toLowerCase()) + { + case LIVE_NODES: + return StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + case VERSIONS: + return STORE_REF_VERSION2_SPACESSTORE; + case DELETED: + return StoreRef.STORE_REF_ARCHIVE_SPACESSTORE; + } + } + throw new InvalidArgumentException(InvalidArgumentException.DEFAULT_MESSAGE_ID, + new Object[] { ": scope allowed values: nodes,deleted-nodes,versions" }); + } + + /** + * Work out which store this noderef belongs to. + * @param nodeRef + * @return String representing a store + */ + public String getStore(NodeRef nodeRef) + { + if (nodeRef != null) + { + if (StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.equals(nodeRef.getStoreRef())) + { + return LIVE_NODES; + } + + if (STORE_REF_VERSION2_SPACESSTORE.equals(nodeRef.getStoreRef())) + { + return VERSIONS; + } + + if (StoreRef.STORE_REF_ARCHIVE_SPACESSTORE.equals(nodeRef.getStoreRef())) + { + return DELETED; + } + } + + logger.warn("Unknown store ref: "+nodeRef); + return null; + } +} diff --git a/source/java/org/alfresco/rest/api/search/model/Scope.java b/source/java/org/alfresco/rest/api/search/model/Scope.java index 953ff349e6..78da76c23e 100644 --- a/source/java/org/alfresco/rest/api/search/model/Scope.java +++ b/source/java/org/alfresco/rest/api/search/model/Scope.java @@ -37,16 +37,16 @@ import java.util.List; */ public class Scope { - private final List stores; + private final List locations; @JsonCreator - public Scope(@JsonProperty("stores") List stores) + public Scope(@JsonProperty("locations") List locations) { - this.stores = stores; + this.locations = locations; } - public List getStores() + public List getLocations() { - return stores; + return locations; } } diff --git a/source/java/org/alfresco/rest/api/trashcan/TrashcanEntityResource.java b/source/java/org/alfresco/rest/api/trashcan/TrashcanEntityResource.java index 2d134e15ba..edd7bdf37e 100644 --- a/source/java/org/alfresco/rest/api/trashcan/TrashcanEntityResource.java +++ b/source/java/org/alfresco/rest/api/trashcan/TrashcanEntityResource.java @@ -77,7 +77,7 @@ public class TrashcanEntityResource implements @Override public Node readById(String nodeId, Parameters parameters) throws EntityNotFoundException { - return deletedNodes.getDeletedNode(nodeId, parameters); + return deletedNodes.getDeletedNode(nodeId, parameters, true, null); } @Operation("restore") diff --git a/source/test-java/org/alfresco/rest/api/search/AllSearchApiTests.java b/source/test-java/org/alfresco/rest/api/search/AllSearchApiTests.java index e9a5f41344..57c7cb17ee 100644 --- a/source/test-java/org/alfresco/rest/api/search/AllSearchApiTests.java +++ b/source/test-java/org/alfresco/rest/api/search/AllSearchApiTests.java @@ -36,7 +36,7 @@ import org.junit.runners.Suite.SuiteClasses; */ @RunWith(Suite.class) @SuiteClasses({ SearchMapperTests.class, ResultMapperTests.class,SearchQuerySerializerTests.class, - SearchApiWebscriptTests.class}) + SearchApiWebscriptTests.class, StoreMapperTests.class}) public class AllSearchApiTests { } diff --git a/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java b/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java index 36bcc743ff..2268c4f0cb 100644 --- a/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java +++ b/source/test-java/org/alfresco/rest/api/search/ResultMapperTests.java @@ -31,22 +31,30 @@ import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyString; 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.security.authentication.AuthenticationUtil; +import org.alfresco.repo.version.Version2Model; +import org.alfresco.repo.version.common.VersionImpl; +import org.alfresco.rest.api.DeletedNodes; import org.alfresco.rest.api.impl.NodesImpl; import org.alfresco.rest.api.lookups.PropertyLookupRegistry; import org.alfresco.rest.api.model.Node; import org.alfresco.rest.api.model.UserInfo; +import org.alfresco.rest.api.nodes.NodeVersionsRelation; import org.alfresco.rest.api.search.context.FacetFieldContext; import org.alfresco.rest.api.search.context.FacetQueryContext; import org.alfresco.rest.api.search.context.SearchContext; import org.alfresco.rest.api.search.context.SpellCheckContext; import org.alfresco.rest.api.search.impl.ResultMapper; +import org.alfresco.rest.api.search.impl.StoreMapper; import org.alfresco.rest.api.search.model.HighlightEntry; +import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Params; import org.alfresco.service.ServiceRegistry; @@ -58,6 +66,9 @@ import org.alfresco.service.cmr.search.GeneralHighlightParameters; 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.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.json.JSONException; @@ -95,6 +106,9 @@ public class ResultMapperTests + "}," + "\"processedDenies\":true, \"lastIndexedTx\":34}"; public static final Params EMPTY_PARAMS = Params.valueOf((String)null,(String)null,(WebScriptRequest) null); + public static final String FROZEN_ID = "frozen"; + public static final String FROZEN_VER = "1.1"; + private static final long VERSIONED_ID = 521l; @BeforeClass public static void setupTests() throws Exception @@ -105,8 +119,57 @@ public class ResultMapperTests NodesImpl nodes = mock(NodesImpl.class); ServiceRegistry sr = mock(ServiceRegistry.class); + DeletedNodes deletedNodes = mock(DeletedNodes.class); nodes.setServiceRegistry(sr); + VersionService versionService = mock(VersionService.class); + VersionHistory versionHistory = mock(VersionHistory.class); + Map versionProperties = new HashMap<>(); + versionProperties.put(Version.PROP_DESCRIPTION, "ver desc"); + versionProperties.put(Version2Model.PROP_VERSION_TYPE, "v type"); + when(versionHistory.getVersion(anyString())).thenAnswer(invocation -> + { + return new VersionImpl(versionProperties,new NodeRef(StoreMapper.STORE_REF_VERSION2_SPACESSTORE, GUID.generate())); + }); + NodeService nodeService = mock(NodeService.class); + + when(versionService.getVersionHistory(notNull(NodeRef.class))).thenAnswer(invocation -> + { + Object[] args = invocation.getArguments(); + NodeRef aNode = (NodeRef)args[0]; + return versionHistory; + }); + + when(nodeService.getProperties(notNull(NodeRef.class))).thenAnswer(invocation -> + { + Object[] args = invocation.getArguments(); + NodeRef aNode = (NodeRef)args[0]; + if (StoreMapper.STORE_REF_VERSION2_SPACESSTORE.equals(aNode.getStoreRef())) + { + nodeProps.put(Version2Model.PROP_QNAME_FROZEN_NODE_REF, new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, FROZEN_ID+aNode.getId())); + nodeProps.put(Version2Model.PROP_QNAME_VERSION_LABEL, FROZEN_VER); + } + return nodeProps; + }); + + when(sr.getVersionService()).thenReturn(versionService); + when(sr.getNodeService()).thenReturn(nodeService); + + when(nodes.validateOrLookupNode(notNull(String.class), anyString())).thenAnswer(invocation -> + { + Object[] args = invocation.getArguments(); + String aNode = (String)args[0]; + if (aNode.endsWith(""+VERSIONED_ID)) + { + throw new EntityNotFoundException(""+VERSIONED_ID); + } + else + { + return new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, aNode); + } + }); + + // // NodeRef nodeRef = nodes.validateOrLookupNode(nodeId, null); when(nodes.getFolderOrDocument(notNull(NodeRef.class), any(), any(), any(), any())).thenAnswer(new Answer() { @Override public Node answer(InvocationOnMock invocation) throws Throwable { @@ -120,9 +183,28 @@ public class ResultMapperTests return new Node(aNode, (NodeRef)args[1], nodeProps, mapUserInfo, sr); } }); + + when(deletedNodes.getDeletedNode(notNull(String.class), any(), anyBoolean(), any())).thenAnswer(new Answer() { + @Override + public Node answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + String nodeId = (String)args[0]; + if (FROZEN_ID.equals(nodeId)) throw new EntityNotFoundException(nodeId); + NodeRef aNode = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, nodeId); + return new Node(aNode, new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE,"unknown"), nodeProps, mapUserInfo, sr); + } + }); mapper = new ResultMapper(); mapper.setNodes(nodes); + mapper.setStoreMapper(new StoreMapper()); mapper.setPropertyLookup(new PropertyLookupRegistry()); + mapper.setDeletedNodes(deletedNodes); + mapper.setServiceRegistry(sr); + NodeVersionsRelation nodeVersionsRelation = new NodeVersionsRelation(); + nodeVersionsRelation.setNodes(nodes); + nodeVersionsRelation.setServiceRegistry(sr); + nodeVersionsRelation.afterPropertiesSet(); + mapper.setNodeVersions(nodeVersionsRelation); } @Test @@ -138,13 +220,14 @@ public class ResultMapperTests @Test public void testToCollectionWithPagingInfo() throws Exception { - ResultSet results = mockResultset(Arrays.asList(514l)); + ResultSet results = mockResultset(Arrays.asList(514l), Arrays.asList(566l, VERSIONED_ID)); CollectionWithPagingInfo collectionWithPage = mapper.toCollectionWithPagingInfo(EMPTY_PARAMS,results); assertNotNull(collectionWithPage); Long found = results.getNumberFound(); assertEquals(found.intValue(), collectionWithPage.getTotalItems().intValue()); Node firstNode = collectionWithPage.getCollection().stream().findFirst().get(); assertNotNull(firstNode.getSearch().getScore()); + assertEquals(StoreMapper.LIVE_NODES, firstNode.getLocation()); collectionWithPage.getCollection().stream().forEach(aNode -> { List high = aNode.getSearch().getHighlight(); if (high != null) @@ -155,13 +238,18 @@ public class ResultMapperTests assertNotNull(first.getSnippets()); } }); + //1 deleted node in the test data + assertEquals(1l, collectionWithPage.getCollection().stream().filter(node -> StoreMapper.DELETED.equals(node.getLocation())).count()); + + //1 version nodes in the test data (and 1 is not shown because it is in the archive store) + assertEquals(1l, collectionWithPage.getCollection().stream().filter(node -> StoreMapper.VERSIONS.equals(node.getLocation())).count()); } @Test public void testToSearchContext() throws Exception { - ResultSet results = mockResultset(Collections.emptyList()); - SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results); + ResultSet results = mockResultset(Collections.emptyList(),Collections.emptyList()); + SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, 0); assertEquals(34l, searchContext.getConsistency().getlastTxId()); assertEquals(6, searchContext.getFacetQueries().size()); // assertEquals("{!afts}creator:admin",searchContext.getFacetQueries().get(0).getLabel()); @@ -209,7 +297,7 @@ public class ResultMapperTests assertEquals(")",sp.getHighlight().getFields().get(1).getPostfix()); } - private ResultSet mockResultset(List archivedNodes) throws JSONException + private ResultSet mockResultset(List archivedNodes, List versionNodes) throws JSONException { NodeService nodeService = mock(NodeService.class); @@ -218,7 +306,8 @@ public class ResultMapperTests public NodeRef answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); //If the DBID is in the list archivedNodes, instead of returning a noderef return achivestore noderef - if (archivedNodes.contains(args[0])) return new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, GUID.generate());; + if (archivedNodes.contains(args[0])) return new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, GUID.generate()); + if (versionNodes.contains(args[0])) return new NodeRef(StoreMapper.STORE_REF_VERSION2_SPACESSTORE, GUID.generate()+args[0]); return new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, GUID.generate()); } }); diff --git a/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java b/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java index 5f26a99dc3..b932b99bad 100644 --- a/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java +++ b/source/test-java/org/alfresco/rest/api/search/SearchMapperTests.java @@ -29,14 +29,13 @@ 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.repository.StoreRef.PROTOCOL_DELETED; -import static org.alfresco.service.cmr.repository.StoreRef.PROTOCOL_TEST; 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 static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import org.alfresco.rest.api.search.impl.SearchMapper; +import org.alfresco.rest.api.search.impl.StoreMapper; import org.alfresco.rest.api.search.model.Default; import org.alfresco.rest.api.search.model.FacetField; import org.alfresco.rest.api.search.model.FacetFields; @@ -58,13 +57,11 @@ import org.alfresco.service.cmr.search.LimitBy; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchParameters.FieldFacet; import org.alfresco.service.cmr.search.SearchService; -import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; import java.util.Arrays; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Tests the SearchMapper class @@ -77,6 +74,12 @@ public class SearchMapperTests static SearchMapper searchMapper = new SearchMapper(); static SerializerTestHelper helper = new SerializerTestHelper(); + @BeforeClass + public static void setupTests() throws Exception + { + searchMapper.setStoreMapper(new StoreMapper()); + } + @Test(expected = IllegalArgumentException.class) public void testMandatory() throws Exception { @@ -431,12 +434,11 @@ public class SearchMapperTests assertNotNull(iae); } - searchMapper.fromScope(searchParameters, new Scope(Arrays.asList( - new StoreRef(PROTOCOL_TEST, "SpacesStore").toString(), - new StoreRef(PROTOCOL_DELETED, "SpacesStore").toString()))); - assertEquals(2 ,searchParameters.getStores().size()); - assertEquals("test://SpacesStore",searchParameters.getStores().get(0).toString()); - assertEquals("deleted://SpacesStore",searchParameters.getStores().get(1).toString()); + searchMapper.fromScope(searchParameters, new Scope(Arrays.asList(StoreMapper.DELETED, StoreMapper.LIVE_NODES, StoreMapper.VERSIONS))); + assertEquals(3 ,searchParameters.getStores().size()); + assertEquals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE.toString(),searchParameters.getStores().get(0).toString()); + assertEquals(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.toString(),searchParameters.getStores().get(1).toString()); + assertEquals(StoreMapper.STORE_REF_VERSION2_SPACESSTORE.toString(),searchParameters.getStores().get(2).toString()); } @Test diff --git a/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java b/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java index 1b94920683..9900f804af 100644 --- a/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java +++ b/source/test-java/org/alfresco/rest/api/search/SearchQuerySerializerTests.java @@ -31,6 +31,7 @@ import static org.junit.Assert.assertTrue; import org.alfresco.rest.api.search.context.FacetFieldContext; import org.alfresco.rest.api.search.context.FacetFieldContext.Bucket; import org.alfresco.rest.api.search.context.SpellCheckContext; +import org.alfresco.rest.api.search.impl.StoreMapper; import org.alfresco.rest.api.search.model.Default; import org.alfresco.rest.api.search.model.FacetField; import org.alfresco.rest.api.search.model.SearchQuery; @@ -92,8 +93,8 @@ public class SearchQuerySerializerTests assertEquals("facquery",searchQuery.getFacetQueries().get(0).getQuery()); assertEquals("facnoused",searchQuery.getFacetQueries().get(0).getLabel()); assertEquals("alfrezco", searchQuery.getSpellcheck().getQuery()); - assertEquals(1, searchQuery.getScope().getStores().size()); - assertEquals("workspace://SpacesStore", searchQuery.getScope().getStores().get(0)); + assertEquals(1, searchQuery.getScope().getLocations().size()); + assertEquals(StoreMapper.LIVE_NODES, searchQuery.getScope().getLocations().get(0)); assertEquals(2, searchQuery.getFacetFields().getFacets().size()); FacetField ff = searchQuery.getFacetFields().getFacets().get(0); assertEquals("cm:creator", ff.getField()); diff --git a/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java b/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java index 47068ba871..c1c49d1763 100644 --- a/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java +++ b/source/test-java/org/alfresco/rest/api/search/SerializerTestHelper.java @@ -64,7 +64,7 @@ public class SerializerTestHelper implements RequestReader + "\"facetQueries\": [{\"query\": \"facquery\",\"label\": \"facnoused\"}]," + "\"spellcheck\": {\"query\": \"alfrezco\"}," + "\"limits\": {\"permissionEvaluationCount\": \"2000\",\"permissionEvaluationTime\": \"5000\"}," - + "\"scope\": { \"stores\": [\"workspace://SpacesStore\"]}," + + "\"scope\": { \"locations\": [\"nodes\"]}," + "\"fields\": [\"id\", \"name\"]," + "\"highlight\": {\"prefix\": \"[\",\"postfix\": \"]\",\"snippetCount\": \"20\"," + "\"fragmentSize\": \"10\",\"mergeContiguous\": \"true\",\"maxAnalyzedChars\": \"40\", \"usePhraseHighlighter\": \"true\"," diff --git a/source/test-java/org/alfresco/rest/api/search/StoreMapperTests.java b/source/test-java/org/alfresco/rest/api/search/StoreMapperTests.java new file mode 100644 index 0000000000..513a54d8dc --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/search/StoreMapperTests.java @@ -0,0 +1,88 @@ +/*- + * #%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 org.junit.Assert.assertNull; +import org.alfresco.rest.api.search.impl.StoreMapper; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.junit.Test; + +/** + * Tests the StoreMapper class + * + * @author Gethin James + */ +public class StoreMapperTests +{ + static StoreMapper storeMapper = new StoreMapper(); + + @Test(expected = InvalidArgumentException.class) + public void testGetStoreErrors() throws Exception + { + storeMapper.getStoreRef(null); + } + + @Test(expected = InvalidArgumentException.class) + public void testGetStoreWithEmpty() throws Exception + { + storeMapper.getStoreRef(""); + } + + @Test(expected = InvalidArgumentException.class) + public void testInvalidStoreName() throws Exception + { + storeMapper.getStoreRef("bob"); + } + + @Test + public void testGetStoreRef() throws Exception + { + assertEquals(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, storeMapper.getStoreRef("nodes")); + assertEquals(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, storeMapper.getStoreRef("Nodes")); + assertEquals(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, storeMapper.getStoreRef("NODES")); + + assertEquals(StoreMapper.STORE_REF_VERSION2_SPACESSTORE, storeMapper.getStoreRef("Versions")); + assertEquals(StoreMapper.STORE_REF_VERSION2_SPACESSTORE, storeMapper.getStoreRef("versions")); + assertEquals(StoreMapper.STORE_REF_VERSION2_SPACESSTORE, storeMapper.getStoreRef("VERSIONS")); + + assertEquals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, storeMapper.getStoreRef("Deleted-nodes")); + assertEquals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, storeMapper.getStoreRef("deleted-nodes")); + assertEquals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, storeMapper.getStoreRef("DELETED-NODES")); + } + + @Test + public void testGetStore() throws Exception + { + assertEquals(storeMapper.LIVE_NODES, storeMapper.getStore(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "not interested"))); + assertEquals(storeMapper.VERSIONS, storeMapper.getStore(new NodeRef(StoreMapper.STORE_REF_VERSION2_SPACESSTORE, "not interested"))); + assertEquals(storeMapper.DELETED, storeMapper.getStore(new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, "not interested"))); + + assertNull(storeMapper.getStore(null)); + } +}