Merged HEAD (5.2) to 5.2.N (5.2.1)

126351 jkaabimofrad: Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2)
      118825 jkaabimofrad: RA-655: manual merge of SFS module to FILE-FOLDER-API.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@126696 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Ancuta Morarasu
2016-05-11 10:46:38 +00:00
parent 38f735e453
commit d05f7bb484
11 changed files with 1793 additions and 921 deletions

View File

@@ -429,9 +429,22 @@
</property>
</bean>
<bean id="nodes" class="org.alfresco.rest.api.impl.NodesImpl">
<bean id="nodes.ignoreTypes" class="org.springframework.beans.factory.config.SetFactoryBean">
<property name="sourceSet">
<set>
<value>cm:systemfolder</value>
<value>fm:forums</value>
<value>fm:forum</value>
<value>fm:topic</value>
<value>fm:post</value>
</set>
</property>
</bean>
<bean id="nodes" class="org.alfresco.rest.api.impl.NodesImpl" init-method="init">
<property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="repositoryHelper" ref="repositoryHelper"/>
<property name="ignoreTypes" ref="nodes.ignoreTypes"/>
</bean>
<bean id="Nodes" class="org.springframework.aop.framework.ProxyFactoryBean">

View File

@@ -36,6 +36,7 @@ import org.alfresco.rest.api.model.Document;
import org.alfresco.rest.api.model.Folder;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.PathInfo;
import org.alfresco.rest.api.model.PathInfo.ElementInfo;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.content.BasicContentInfo;
@@ -53,11 +54,14 @@ import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.Path.Element;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
@@ -76,6 +80,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
@@ -83,6 +88,7 @@ import java.util.Set;
*
* @author steveglover
* @author janv
* @author Jamal Kaabi-Mofrad
*
* @since publicapi1.0
*/
@@ -105,6 +111,20 @@ public class NodesImpl implements Nodes
private PermissionService permissionService;
private Repository repositoryHelper;
private ServiceRegistry sr;
private Set<String> defaultIgnoreTypes;
private Set<QName> ignoreTypeQNames;
public void init()
{
if (defaultIgnoreTypes != null)
{
ignoreTypeQNames = new HashSet<>(defaultIgnoreTypes.size());
for (String type : defaultIgnoreTypes)
{
ignoreTypeQNames.add(createQName(type));
}
}
}
public void setServiceRegistry(ServiceRegistry sr) {
this.sr = sr;
@@ -121,6 +141,11 @@ public class NodesImpl implements Nodes
this.repositoryHelper = repositoryHelper;
}
public void setIgnoreTypes(Set<String> ignoreTypes)
{
this.defaultIgnoreTypes = ignoreTypes;
}
private static final List<QName> EXCLUDED_ASPECTS = Arrays.asList(
ContentModel.ASPECT_REFERENCEABLE,
ContentModel.ASPECT_LOCALIZED);
@@ -176,7 +201,6 @@ public class NodesImpl implements Nodes
new HashSet<>(Arrays.asList(new String[] {PARAM_ISFOLDER}));
/*
*
* Note: assumes workspace://SpacesStore
*/
public NodeRef validateNode(String nodeId)
@@ -274,8 +298,8 @@ public class NodesImpl implements Nodes
private Type getType(QName type)
{
boolean isContainer = Boolean.valueOf((dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
!dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER)));
boolean isContainer = Boolean.valueOf((dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true
&& !dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER)));
return isContainer ? Type.FOLDER : Type.DOCUMENT;
}
@@ -347,7 +371,8 @@ public class NodesImpl implements Nodes
return nodeService.getPrimaryParent(nodeRef).getParentRef();
}
private NodeRef validateOrLookupNode(String nodeId, String path) {
private NodeRef validateOrLookupNode(String nodeId, String path)
{
NodeRef parentNodeRef;
if (nodeId.equals(PATH_ROOT))
@@ -363,16 +388,21 @@ public class NodesImpl implements Nodes
NodeRef person = repositoryHelper.getPerson();
if (person == null)
{
throw new IllegalArgumentException("Unexpected: cannot use "+PATH_MY);
throw new InvalidArgumentException("Unexpected: cannot use " + PATH_MY);
}
parentNodeRef = repositoryHelper.getUserHome(person);
if (parentNodeRef == null)
{
throw new EntityNotFoundException(nodeId);
}
}
else
{
parentNodeRef = validateNode(nodeId);
}
if (path != null) {
if (path != null)
{
// resolve path relative to current nodeId
parentNodeRef = resolveNodeByPath(parentNodeRef, path, true);
}
@@ -438,10 +468,11 @@ public class NodesImpl implements Nodes
NodeRef nodeRef = validateOrLookupNode(nodeId, path);
QName typeQName = nodeService.getType(nodeRef);
return getFolderOrDocument(nodeRef, getParentNodeRef(nodeRef), typeQName, false);
List<QName> requestedProperties = createQNames(parameters.getSelectedProperties());
return getFolderOrDocument(nodeRef, getParentNodeRef(nodeRef), typeQName, requestedProperties, false);
}
private Node getFolderOrDocument(final NodeRef nodeRef, NodeRef parentNodeRef, QName typeQName, boolean minimalnfo)
private Node getFolderOrDocument(final NodeRef nodeRef, NodeRef parentNodeRef, QName typeQName, List<QName> selectedProperties, boolean minimalnfo)
{
PathInfo pathInfo = null;
if (!minimalnfo)
@@ -468,8 +499,9 @@ public class NodesImpl implements Nodes
throw new InvalidArgumentException("Node is not a folder or file");
}
if (! minimalnfo) {
node.setProperties(mapProperties(properties));
if (!minimalnfo)
{
node.setProperties(mapProperties(properties, selectedProperties));
node.setAspectNames(mapAspects(nodeService.getAspects(nodeRef)));
}
@@ -481,66 +513,124 @@ public class NodesImpl implements Nodes
protected PathInfo lookupPathInfo(NodeRef nodeRefIn)
{
List<PathInfo.ElementInfo> elements = new ArrayList<>(5);
// TODO which implementation?
return getPathInfo(nodeRefIn);
NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();
boolean isComplete = true;
NodeRef pNodeRef = nodeRefIn;
while (pNodeRef != null)
{
if (pNodeRef.equals(companyHomeNodeRef))
{
pNodeRef = null;
// List<PathInfo.ElementInfo> elements = new ArrayList<>(5);
//
// NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();
// boolean isComplete = true;
//
// NodeRef pNodeRef = nodeRefIn;
// while (pNodeRef != null)
// {
// if (pNodeRef.equals(companyHomeNodeRef))
// {
// pNodeRef = null;
// }
// else {
// pNodeRef = nodeService.getPrimaryParent(pNodeRef).getParentRef();
//
// if (pNodeRef == null)
// {
// // belts-and-braces - is it even possible to get here ?
// isComplete = false;
// }
// else
// {
// if (permissionService.hasPermission(pNodeRef, PermissionService.READ)
// == AccessStatus.ALLOWED)
// {
// String name = (String) nodeService.getProperty(pNodeRef,
// ContentModel.PROP_NAME);
// elements.add(0, new ElementInfo(pNodeRef, name));
// }
// else
// {
// isComplete = false;
// pNodeRef = null;
// }
// }
// }
// }
//
// StringBuilder sb = new StringBuilder();
// for (PathInfo.ElementInfo e : elements)
// {
// sb.append("/").append(e.getName());
// }
//
// return new PathInfo(sb.toString(), isComplete, elements);
}
else {
pNodeRef = nodeService.getPrimaryParent(pNodeRef).getParentRef();
if (pNodeRef == null)
private PathInfo getPathInfo(NodeRef nodeRef)
{
// belts-and-braces - is it even possible to get here ?
isComplete = false;
final Path nodePath = nodeService.getPath(nodeRef);
List<ElementInfo> pathElements = new ArrayList<>();
Boolean isComplete = Boolean.TRUE;
// 2 => as we don't want to include the given node in the path as well.
for (int i = nodePath.size() - 2; i >= 0; i--)
{
Element element = nodePath.get(i);
if (element instanceof Path.ChildAssocElement)
{
ChildAssociationRef elementRef = ((Path.ChildAssocElement) element).getRef();
if (elementRef.getParentRef() != null)
{
NodeRef childNodeRef = elementRef.getChildRef();
if (permissionService.hasPermission(childNodeRef, PermissionService.READ) == AccessStatus.ALLOWED)
{
Serializable nameProp = nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
pathElements.add(0, new ElementInfo(childNodeRef, nameProp.toString()));
}
else
{
if (permissionService.hasPermission(pNodeRef, PermissionService.READ) == AccessStatus.ALLOWED)
{
String name = (String) nodeService.getProperty(pNodeRef, ContentModel.PROP_NAME);
elements.add(0, new PathInfo().new ElementInfo(pNodeRef.getId(), name));
}
else
{
isComplete = false;
pNodeRef = null;
// Just return the pathInfo up to the location where the user has access
isComplete = Boolean.FALSE;
break;
}
}
}
}
StringBuilder sb = new StringBuilder();
for (PathInfo.ElementInfo e : elements)
String pathStr = null;
if(pathElements.size() > 0)
{
StringBuilder sb = new StringBuilder(120);
for (PathInfo.ElementInfo e : pathElements)
{
sb.append("/").append(e.getName());
}
return new PathInfo(sb.toString(), isComplete, elements);
pathStr = sb.toString();
}
else
{
// There is no path element, so set it to null in order to be
// ignored by Jackson during serialisation
isComplete = null;
}
return new PathInfo(pathStr, isComplete, pathElements);
}
protected Map<String, Serializable> mapProperties(Map<QName, Serializable> nodeProps)
protected Map<String, Object> mapProperties(Map<QName, Serializable> nodeProps, List<QName> selectedProperties)
{
Map<String, Serializable> props = new HashMap<>(nodeProps.size());
for (Map.Entry<QName, Serializable> entry : nodeProps.entrySet()) {
QName propQName = entry.getKey();
if (!EXCLUDED_PROPS.contains(propQName))
Map<String, Object> props = null;
if (!selectedProperties.isEmpty())
{
props.put(entry.getKey().toPrefixString(namespaceService), entry.getValue());
props = new HashMap<>(selectedProperties.size());
for (QName qName : selectedProperties)
{
Serializable value = nodeProps.get(qName);
if (value != null)
{
props.put(qName.toPrefixString(namespaceService), value);
}
}
if (props.size() == 0)
if (props.isEmpty())
{
props = null; // no props to return
props = null; // set to null so it doesn't show up as an empty object in the JSON response.
}
}
return props;
@@ -568,12 +658,19 @@ public class NodesImpl implements Nodes
public CollectionWithPagingInfo<Node> getChildren(String parentFolderNodeId, Parameters parameters)
{
// TODO do we want to support path with list folder children ?
String path = null;
// String path = parameters.getParameter("path");
final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, path);
// TODO
// map - where (filter) properties - including isFolder
// map - orderBy (sort) properties - including isFolder
// TODO refactor & fix !
final boolean minimalnfo = (parameters.getSelectedProperties().size() == 0);
final List<QName> requestedProperties = createQNames(parameters.getSelectedProperties());
boolean includeFolders = true;
boolean includeFiles = true;
@@ -604,7 +701,7 @@ public class NodesImpl implements Nodes
QName propQname = MAP_PARAM_QNAME.get(sortCol.column);
if (propQname == null)
{
propQname = QName.resolveToQName(namespaceService, sortCol.column);
propQname = createQName(sortCol.column);
}
if (propQname != null)
@@ -623,12 +720,6 @@ public class NodesImpl implements Nodes
Paging paging = parameters.getPaging();
// TODO do we want to support path with list folder children ?
String path = null;
//String path = parameters.getParameter("path");
final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, path);
if (! nodeMatches(parentNodeRef, Collections.singleton(ContentModel.TYPE_FOLDER), null))
{
throw new InvalidArgumentException("NodeId of folder is expected");
@@ -636,7 +727,7 @@ public class NodesImpl implements Nodes
PagingRequest pagingRequest = Util.getPagingRequest(paging);
final PagingResults<FileInfo> pagingResults = fileFolderService.list(parentNodeRef, includeFiles, includeFolders, null, sortProps, pagingRequest);
final PagingResults<FileInfo> pagingResults = fileFolderService.list(parentNodeRef, includeFiles, includeFolders, ignoreTypeQNames, sortProps, pagingRequest);
final List<FileInfo> page = pagingResults.getPage();
List<Node> nodes = new AbstractList<Node>()
@@ -647,7 +738,7 @@ public class NodesImpl implements Nodes
FileInfo fInfo = page.get(index);
// basic info by default (unless "select"ed otherwise)
return getFolderOrDocument(fInfo.getNodeRef(), parentNodeRef, fInfo.getType(), minimalnfo);
return getFolderOrDocument(fInfo.getNodeRef(), parentNodeRef, fInfo.getType(), requestedProperties, minimalnfo);
}
@Override
@@ -690,7 +781,7 @@ public class NodesImpl implements Nodes
}
// check that requested type is a (sub-) type of folder or content
QName nodeTypeQName = QName.resolveToQName(namespaceService, nodeType);
QName nodeTypeQName = createQName(nodeType);
Set<QName> contentAndFolders = new HashSet<>(Arrays.asList(ContentModel.TYPE_FOLDER, ContentModel.TYPE_CONTENT));
if (! typeMatches(nodeTypeQName, contentAndFolders, null)) {
@@ -703,7 +794,7 @@ public class NodesImpl implements Nodes
if (nodeInfo.getProperties() != null)
{
for (Map.Entry entry : (Set<Map.Entry<String, Serializable>>)nodeInfo.getProperties().entrySet())
for (Entry<String, Object> entry : nodeInfo.getProperties().entrySet())
{
QName propQName = QName.createQName((String)entry.getKey(), namespaceService);
props.put(propQName, (Serializable)entry.getValue());
@@ -741,7 +832,7 @@ public class NodesImpl implements Nodes
if (nodeInfo.getProperties() != null)
{
for (Map.Entry entry : (Set<Map.Entry<String, Serializable>>)nodeInfo.getProperties().entrySet())
for (Entry<String, Object> entry : nodeInfo.getProperties().entrySet())
{
QName propQName = QName.createQName((String)entry.getKey(), namespaceService);
props.put(propQName, (Serializable)entry.getValue());
@@ -807,4 +898,58 @@ public class NodesImpl implements Nodes
// TODO - hmm - we may wish to return json info !!
return;
}
/**
* Helper to create a QName from either a fully qualified or short-name QName string
*
* @param qnameStr Fully qualified or short-name QName string
* @return QName
*/
protected QName createQName(String qnameStr)
{
try
{
QName qname;
if (qnameStr.indexOf(QName.NAMESPACE_BEGIN) != -1)
{
qname = QName.createQName(qnameStr);
}
else
{
qname = QName.createQName(qnameStr, namespaceService);
}
return qname;
}
catch (Exception ex)
{
String msg = ex.getMessage();
if (msg == null)
{
msg = "";
}
throw new InvalidArgumentException(qnameStr + " isn't a valid QName. " + msg);
}
}
/**
* Helper to create a QName from either a fully qualified or short-name QName string
*
* @param qnameStrList list of fully qualified or short-name QName string
* @return a list of {@code QName} objects
*/
protected List<QName> createQNames(List<String> qnameStrList)
{
List<QName> result = new ArrayList<>(qnameStrList.size());
for (String str : qnameStrList)
{
str = str.replaceFirst("_", ":"); // FIXME remove this when we have fixed the framework.
QName name = createQName(str);
if (!EXCLUDED_PROPS.contains(name))
{
result.add(name);
}
}
return result;
}
}

View File

@@ -46,6 +46,11 @@ public class Document extends Node
{
private ContentInfo contentInfo;
// instance init block
{
this.isFolder = Boolean.FALSE;
}
public Document() {
super();
}
@@ -64,16 +69,16 @@ public class Document extends Node
}
}
public Boolean getIsFolder()
{
return false;
}
public ContentInfo getContent()
{
return contentInfo;
}
public void setContent(ContentInfo contentInfo)
{
this.contentInfo = contentInfo;
}
@Override
public String toString()
{

View File

@@ -23,6 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.model;
import java.io.Serializable;
@@ -37,10 +38,14 @@ import org.alfresco.service.namespace.QName;
*
* @author steveglover
* @author janv
*
*/
public class Folder extends Node
{
// instance init block
{
this.isFolder = Boolean.TRUE;
}
public Folder()
{
super();
@@ -51,11 +56,6 @@ public class Folder extends Node
super(nodeRef, parentNodeRef, nodeProps, sr);
}
public Boolean getIsFolder()
{
return true;
}
@Override
public String toString()
{

View File

@@ -57,13 +57,15 @@ public class Node implements Comparable<Node>
protected UserInfo createdByUser;
protected UserInfo modifiedByUser;
protected Boolean isFolder;
protected NodeRef parentNodeRef;
protected PathInfo pathInfo;
protected String prefixTypeQName;
protected List<String> aspectNames;
protected Map<String, Serializable> props;
protected Map<String, Object> properties;
// TODO fixme !
// also need to optionally pass in user map - eg. when listing children (to avoid multiple lookups for same user)
@@ -190,12 +192,12 @@ public class Node implements Comparable<Node>
this.prefixTypeQName = prefixType;
}
public Map getProperties() {
return this.props;
public Map<String, Object> getProperties() {
return this.properties;
}
public void setProperties(Map props) {
this.props = props;
public void setProperties(Map<String, Object> props) {
this.properties = props;
}
public List<String> getAspectNames() {
@@ -211,6 +213,21 @@ public class Node implements Comparable<Node>
return parentNodeRef;
}
public void setParentId(NodeRef parentNodeRef)
{
this.parentNodeRef = parentNodeRef;
}
public Boolean getIsFolder()
{
return isFolder;
}
public void setIsFolder(Boolean isFolder)
{
this.isFolder=isFolder;
}
public boolean equals(Object other)
{
if(this == other)

View File

@@ -1,12 +1,14 @@
package org.alfresco.rest.api.model;
import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Representation of a path info
*
* @author janv
*
*/
public class PathInfo
{
@@ -25,40 +27,66 @@ public class PathInfo
this.elements = elements;
}
public String getName() {
public String getName()
{
return name;
}
public Boolean getIsComplete() {
public Boolean getIsComplete()
{
return isComplete;
}
public List<ElementInfo> getElements() {
public List<ElementInfo> getElements()
{
return elements;
}
public class ElementInfo {
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder(120);
sb.append("PathInfo [name='").append(name)
.append(", isComplete=").append(isComplete)
.append(", elements=").append(elements)
.append(']');
return sb.toString();
}
private String id;
public static class ElementInfo
{
private NodeRef id;
private String name;
public ElementInfo()
{
}
public ElementInfo(String id, String name)
public ElementInfo(NodeRef id, String name)
{
this.id = id;
this.name = name;
}
public String getName() {
public String getName()
{
return name;
}
public String getId() {
public NodeRef getId()
{
return id;
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder(250);
sb.append("PathElement [id=").append(id)
.append(", name='").append(name)
.append(']');
return sb.toString();
}
}
}

View File

@@ -16,13 +16,13 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.rest.api.model;
/**
* Representation of a user info
*
* @author janv
*
*/
public class UserInfo
{
@@ -36,18 +36,19 @@ public class UserInfo
public UserInfo(String userName, String firstName, String lastName)
{
this.userName = userName;
this.displayName = ((firstName != null ? firstName + " " : "") + (lastName != null ? lastName : "")).replaceAll("^\\s+|\\s+$", "");
this.displayName = ((firstName != null ? firstName + " " : "") + (lastName != null ? lastName : "")).trim();
}
public String getDisplayName() {
public String getDisplayName()
{
return displayName;
}
public String getUserName() {
public String getUserName()
{
return userName;
}
@Override
public String toString()
{

View File

@@ -26,10 +26,17 @@
package org.alfresco.rest.api.tests;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertNotNull;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.rest.api.tests.RepoService.SiteInformation;
import org.alfresco.rest.api.tests.RepoService.TestNetwork;
import org.alfresco.rest.api.tests.RepoService.TestPerson;
import org.alfresco.rest.api.tests.RepoService.TestSite;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.rest.api.tests.client.RequestContext;
import org.alfresco.service.cmr.site.SiteVisibility;
import java.util.Map;
@@ -82,9 +89,14 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
}
protected HttpResponse getAll(String url, String runAsUser, PublicApiClient.Paging paging, int expectedStatus) throws Exception
{
return getAll(url, runAsUser, paging, null, expectedStatus);
}
protected HttpResponse getAll(String url, String runAsUser, PublicApiClient.Paging paging, Map<String, String> otherParams, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
Map<String, String> params = (paging == null) ? null : createParams(paging, null);
Map<String, String> params = (paging == null) ? null : createParams(paging, otherParams);
HttpResponse response = publicApiClient.get(getScope(), url, null, null, null, params);
checkStatus(expectedStatus, response.getStatusCode());
@@ -92,6 +104,16 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return response;
}
protected HttpResponse getAll(Class<?> entityResource, String runAsUser, PublicApiClient.Paging paging, Map<String, String> otherParams, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
HttpResponse response = publicApiClient.get(entityResource, null, null, otherParams);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected HttpResponse getSingle(String url, String runAsUser, String entityId, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
@@ -102,6 +124,16 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return response;
}
protected HttpResponse getSingle(Class<?> entityResource, String runAsUser, String entityId, Map<String, String> params, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
HttpResponse response = publicApiClient.get(entityResource, entityId, null, params);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected HttpResponse put(String url, String runAsUser, String entityId, String body, String queryString, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
@@ -132,6 +164,23 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return person.getId();
}
protected TestSite createSite(final TestNetwork testNetwork, TestPerson user, final SiteVisibility siteVisibility)
{
final String siteName = "RandomSite" + System.currentTimeMillis();
final TestSite site = TenantUtil.runAsUserTenant(new TenantUtil.TenantRunAsWork<TestSite>()
{
@Override
public TestSite doWork() throws Exception
{
SiteInformation siteInfo = new SiteInformation(siteName, siteName, siteName, siteVisibility);
return repoService.createSite(testNetwork, siteInfo);
}
}, user.getId(), testNetwork.getId());
assertNotNull(site);
return site;
}
protected void checkStatus(int expectedStatus, int actualStatus)
{
if (expectedStatus > 0 && expectedStatus != actualStatus)

View File

@@ -0,0 +1,485 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.rest.api.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.api.model.Document;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.PathInfo;
import org.alfresco.rest.api.model.PathInfo.ElementInfo;
import org.alfresco.rest.api.model.UserInfo;
import org.alfresco.rest.api.nodes.NodesEntityResource;
import org.alfresco.rest.api.tests.RepoService.TestNetwork;
import org.alfresco.rest.api.tests.RepoService.TestPerson;
import org.alfresco.rest.api.tests.RepoService.TestSite;
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.SiteRole;
import org.alfresco.rest.api.tests.util.JacksonUtil;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* API tests for:
* <ul>
* <li> {@literal host:port/alfresco/api/{networkId}/public/alfresco/versions/1/nodes/{nodeId}} </li>
* <li> {@literal host:port/alfresco/api/{networkId}/public/alfresco/versions/1/nodes/{nodeId}/children} </li>
* </ul>
*
* @author Jamal Kaabi-Mofrad
*/
public class NodeApiTest extends AbstractBaseApiTest
{
/**
* User one from network one
*/
private TestPerson userOneN1;
/**
* User two from network one
*/
private TestPerson userTwoN1;
/**
* Private site of user one from network one
*/
private TestSite userOneN1Site;
private String user1;
private String user2;
private List<String> users = new ArrayList<>();
protected MutableAuthenticationService authenticationService;
protected PersonService personService;
protected Repository repositoryHelper;
protected JacksonUtil jacksonUtil;
protected PermissionService permissionService;
@Before
public void setup() throws Exception
{
authenticationService = applicationContext.getBean("authenticationService", MutableAuthenticationService.class);
personService = applicationContext.getBean("personService", PersonService.class);
repositoryHelper = applicationContext.getBean("repositoryHelper", Repository.class);
jacksonUtil = new JacksonUtil(applicationContext.getBean("jsonHelper", JacksonHelper.class));
permissionService = applicationContext.getBean("permissionService", PermissionService.class);
user1 = createUser("user1" + System.currentTimeMillis());
user2 = createUser("user2" + System.currentTimeMillis());
// We just need to clean the on-premise-users,
// so the tests for the specific network would work.
users.add(user1);
users.add(user2);
TestNetwork networkOne = getTestFixture().getRandomNetwork();
userOneN1 = networkOne.createUser();
userTwoN1 = networkOne.createUser();
userOneN1Site = createSite(networkOne, userOneN1, SiteVisibility.PRIVATE);
}
@After
public void tearDown() throws Exception
{
AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser();
for (final String user : users)
{
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override
public Void execute() throws Throwable
{
if (personService.personExists(user))
{
authenticationService.deleteAuthentication(user);
personService.deletePerson(user);
}
return null;
}
});
}
users.clear();
AuthenticationUtil.clearCurrentSecurityContext();
}
@Test
public void testListDocLibChildren() throws Exception
{
AuthenticationUtil.setFullyAuthenticatedUser(userOneN1.getId());
NodeRef docLibNodeRef = userOneN1Site.getContainerNodeRef(("documentLibrary"));
String folder1 = "folder" + System.currentTimeMillis() + "_1";
repoService.addToDocumentLibrary(userOneN1Site, folder1, ContentModel.TYPE_FOLDER);
String folder2 = "folder" + System.currentTimeMillis() + "_2";
repoService.addToDocumentLibrary(userOneN1Site, folder2, ContentModel.TYPE_FOLDER);
String content1 = "content" + System.currentTimeMillis() + "_1";
repoService.addToDocumentLibrary(userOneN1Site, content1, ContentModel.TYPE_CONTENT);
String content2 = "content" + System.currentTimeMillis() + "_2";
repoService.addToDocumentLibrary(userOneN1Site, content2, ContentModel.TYPE_CONTENT);
String forum1 = "forum" + System.currentTimeMillis() + "_1";
repoService.createObjectOfCustomType(docLibNodeRef, forum1, ForumModel.TYPE_TOPIC.toString());
Paging paging = getPaging(0, 100);
HttpResponse response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, 200);
List<Node> nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class);
assertEquals(4, nodes.size()); // forum is part of the default ignored types
// Paging
ExpectedPaging expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse());
assertEquals(4, expectedPaging.getCount().intValue());
assertEquals(0, expectedPaging.getSkipCount().intValue());
assertEquals(100, expectedPaging.getMaxItems().intValue());
assertFalse(expectedPaging.getHasMoreItems().booleanValue());
// Order by folders and modified date first
Map<String, String> orderBy = Collections.singletonMap("orderBy", "isFolder DESC,modifiedAt DESC");
response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200);
nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class);
assertEquals(4, nodes.size());
assertEquals(folder2, nodes.get(0).getName());
assertTrue(nodes.get(0).getIsFolder());
assertEquals(folder1, nodes.get(1).getName());
assertTrue(nodes.get(1).getIsFolder());
assertEquals(content2, nodes.get(2).getName());
assertFalse(nodes.get(2).getIsFolder());
assertEquals(content1, nodes.get(3).getName());
assertFalse(nodes.get(3).getIsFolder());
// Order by folders last and modified date first
orderBy = Collections.singletonMap("orderBy", "isFolder ASC,modifiedAt DESC");
response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200);
nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class);
assertEquals(4, nodes.size());
assertEquals(content2, nodes.get(0).getName());
assertEquals(content1, nodes.get(1).getName());
assertEquals(folder2, nodes.get(2).getName());
assertEquals(folder1, nodes.get(3).getName());
// Order by folders and modified date last
orderBy = Collections.singletonMap("orderBy", "isFolder,modifiedAt");
response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200);
nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class);
assertEquals(4, nodes.size());
assertEquals(content1, nodes.get(0).getName());
assertEquals(content2, nodes.get(1).getName());
assertEquals(folder1, nodes.get(2).getName());
assertEquals(folder2, nodes.get(3).getName());
// Order by folders and modified date first
orderBy = Collections.singletonMap("orderBy", "isFolder DESC,modifiedAt DESC");
// SkipCount=0,MaxItems=2
paging = getPaging(0, 2);
response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200);
nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class);
assertEquals(2, nodes.size());
assertEquals(folder2, nodes.get(0).getName());
assertEquals(folder1, nodes.get(1).getName());
expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse());
assertEquals(2, expectedPaging.getCount().intValue());
assertEquals(0, expectedPaging.getSkipCount().intValue());
assertEquals(2, expectedPaging.getMaxItems().intValue());
assertTrue(expectedPaging.getHasMoreItems().booleanValue());
// SkipCount=2,MaxItems=4
paging = getPaging(2, 4);
response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200);
nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class);
assertEquals(2, nodes.size());
assertEquals(content2, nodes.get(0).getName());
assertEquals(content1, nodes.get(1).getName());
expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse());
assertEquals(2, expectedPaging.getCount().intValue());
assertEquals(2, expectedPaging.getSkipCount().intValue());
assertEquals(4, expectedPaging.getMaxItems().intValue());
assertFalse(expectedPaging.getHasMoreItems().booleanValue());
// userTwoN1 tries to access userOneN1's docLib
AuthenticationUtil.setFullyAuthenticatedUser(userTwoN1.getId());
paging = getPaging(0, Integer.MAX_VALUE);
getAll(getChildrenUrl(docLibNodeRef), userTwoN1.getId(), paging, 403);
}
@Test
public void testListMyFilesChildren() throws Exception
{
AuthenticationUtil.setFullyAuthenticatedUser(user1);
NodeRef myFilesNodeRef = repositoryHelper.getUserHome(personService.getPerson(user1));
String folder1 = "folder" + System.currentTimeMillis() + "_1";
repoService.createFolder(myFilesNodeRef, folder1);
String folder2 = "folder" + System.currentTimeMillis() + "_2";
repoService.createFolder(myFilesNodeRef, folder2);
String content1 = "content" + System.currentTimeMillis() + "_1";
NodeRef contentNodeRef = repoService.createDocument(myFilesNodeRef, content1, "The quick brown fox jumps over the lazy dog.");
repoService.getNodeService().setProperty(contentNodeRef, ContentModel.PROP_OWNER, user1);
repoService.getNodeService().setProperty(contentNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA,
(Serializable) Collections.singletonList("doclib:1444660852296"));
Paging paging = getPaging(0, Integer.MAX_VALUE);
HttpResponse response = getAll(getChildrenUrl(myFilesNodeRef), user1, paging, 200);
List<Document> nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Document.class);
assertEquals(3, nodes.size());
// Order by folders and modified date first
Map<String, String> orderBy = Collections.singletonMap("orderBy", "isFolder DESC,modifiedAt DESC");
response = getAll(getChildrenUrl(myFilesNodeRef), user1, paging, orderBy, 200);
nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Document.class);
assertEquals(3, nodes.size());
assertEquals(folder2, nodes.get(0).getName());
assertEquals(folder1, nodes.get(1).getName());
Document node = (Document) nodes.get(2);
assertEquals(content1, node.getName());
assertEquals("cm:content", node.getNodeType());
assertEquals(contentNodeRef.getId(), node.getNodeRef().getId());
UserInfo createdByUser = node.getCreatedByUser();
assertEquals(user1, createdByUser.getUserName());
assertEquals(user1 + " " + user1, createdByUser.getDisplayName());
UserInfo modifiedByUser = node.getModifiedByUser();
assertEquals(user1, modifiedByUser.getUserName());
assertEquals(user1 + " " + user1, modifiedByUser.getDisplayName());
assertEquals(MimetypeMap.MIMETYPE_BINARY, node.getContent().getMimeType());
assertNotNull(node.getContent().getMimeTypeName());
assertNotNull(node.getContent().getEncoding());
assertTrue(node.getContent().getSizeInBytes() > 0);
// Invalid QName (Namespace prefix cm... is not mapped to a namespace URI) for the orderBy parameter.
orderBy = Collections.singletonMap("orderBy", "isFolder DESC,cm" + System.currentTimeMillis() + ":modified DESC");
getAll(getChildrenUrl(myFilesNodeRef), user1, paging, orderBy, 400);
AuthenticationUtil.setFullyAuthenticatedUser(user2);
// user2 tries to access user1's home folder
getAll(getChildrenUrl(myFilesNodeRef), user2, paging, 403);
AuthenticationUtil.setFullyAuthenticatedUser(user1);
// request property via select
Map<String, String> params = new LinkedHashMap<>();
params.put("select", "cm_lastThumbnailModification");// TODO replace the underscore with colon when the framework is fixed.
params.put("orderBy", "isFolder DESC,modifiedAt DESC");
response = getAll(getChildrenUrl(myFilesNodeRef), user1, paging, params, 200);
nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Document.class);
assertEquals(3, nodes.size());
assertNull("There shouldn't be a 'properties' object in the response.", nodes.get(0).getProperties());
assertNull("There shouldn't be a 'properties' object in the response.", nodes.get(1).getProperties());
assertNotNull("There should be a 'properties' object in the response.", nodes.get(2).getProperties());
Set<Entry<String, Object>> props = nodes.get(2).getProperties().entrySet();
assertEquals(1, props.size());
Entry<String, Object> entry = props.iterator().next();
assertEquals("cm:lastThumbnailModification", entry.getKey());
assertEquals("doclib:1444660852296", ((List<?>) entry.getValue()).get(0));
}
@Test
public void testGetPathElements_DocLib() throws Exception
{
AuthenticationUtil.setFullyAuthenticatedUser(userOneN1.getId());
userOneN1Site.inviteToSite(userTwoN1.getEmail(), SiteRole.SiteConsumer);
NodeRef docLibNodeRef = userOneN1Site.getContainerNodeRef(("documentLibrary"));
// /Company Home/Sites/RandomSite<timestamp>/documentLibrary/folder<timestamp>_A
String folderA = "folder" + System.currentTimeMillis() + "_A";
NodeRef folderA_Ref = repoService.createFolder(docLibNodeRef, folderA);
// /Company Home/Sites/RandomSite<timestamp>/documentLibrary/folder<timestamp>_A/folder<timestamp>_B
String folderB = "folder" + System.currentTimeMillis() + "_B";
NodeRef folderB_Ref = repoService.createFolder(folderA_Ref, folderB);
// /Company Home/Sites/RandomSite<timestamp>/documentLibrary/folder<timestamp>_A/folder<timestamp>_B/folder<timestamp>_C
String folderC = "folder" + System.currentTimeMillis() + "_C";
NodeRef folderC_Ref = repoService.createFolder(folderB_Ref, folderC);
// /Company Home/Sites/RandomSite<timestamp>/documentLibrary/folder<timestamp>_A/folder<timestamp>_B/folder<timestamp>_C/content<timestamp>
String content = "content" + System.currentTimeMillis();
NodeRef contentNodeRef = repoService.createDocument(folderC_Ref, content, "The quick brown fox jumps over the lazy dog.");
// Revoke folderB inherited permissions
permissionService.setInheritParentPermissions(folderB_Ref, false);
// Grant userTwoN1 permission for folderC
permissionService.setPermission(folderC_Ref, userTwoN1.getId(), PermissionService.CONSUMER, true);
//...nodes/nodeId?select=path
Map<String, String> params = Collections.singletonMap("select", "path");
HttpResponse response = getSingle(NodesEntityResource.class, userOneN1.getId(), contentNodeRef.getId(), params, 200);
Document node = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class);
PathInfo path = node.getPath();
assertNotNull(path);
assertTrue(path.getIsComplete());
assertNotNull(path.getName());
// the path should only include the parents (not the requested node)
assertFalse(path.getName().endsWith(content));
assertTrue(path.getName().startsWith("/Company Home"));
List<ElementInfo> pathElements = path.getElements();
assertEquals(7, pathElements.size());
assertEquals("Company Home", pathElements.get(0).getName());
assertEquals("Sites", pathElements.get(1).getName());
assertEquals(userOneN1Site.getSiteId(), pathElements.get(2).getName());
assertEquals("documentLibrary", pathElements.get(3).getName());
assertEquals(folderA, pathElements.get(4).getName());
assertEquals(folderB, pathElements.get(5).getName());
assertEquals(folderC, pathElements.get(6).getName());
// Try the above tests with userTwoN1 (site consumer)
AuthenticationUtil.setFullyAuthenticatedUser(userTwoN1.getId());
response = getSingle(NodesEntityResource.class, userTwoN1.getId(), contentNodeRef.getId(), params, 200);
node = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class);
path = node.getPath();
assertNotNull(path);
assertFalse("The path is not complete as the user doesn't have permission to access the full path.", path.getIsComplete());
assertNotNull(path.getName());
// site consumer (userTwoN1) dose not have access to the folderB
assertFalse("site consumer (userTwoN1) dose not have access to the folderB", path.getName().contains(folderB));
assertFalse(path.getName().startsWith("/Company Home"));
// Go up as far as they can, before getting access denied (i.e. "/folderC")
assertTrue(path.getName().endsWith(folderC));
pathElements = path.getElements();
assertEquals(1, pathElements.size());
assertEquals(folderC, pathElements.get(0).getName());
}
@Test
public void testGetPathElements_MyFiles() throws Exception
{
final String userNodeAlias = "-my-";
AuthenticationUtil.setFullyAuthenticatedUser(user1);
HttpResponse response = getSingle(NodesEntityResource.class, user1, userNodeAlias, null, 200);
Node node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class);
NodeRef myFilesNodeRef = node.getNodeRef();
assertNotNull(myFilesNodeRef);
assertEquals(user1, node.getName());
assertTrue(node.getIsFolder());
// /Company Home/User Homes/user<timestamp>/folder<timestamp>_A
String folderA = "folder" + System.currentTimeMillis() + "_A";
NodeRef folderA_Ref = repoService.createFolder(myFilesNodeRef, folderA);
// /Company Home/User Homes/user<timestamp>/folder<timestamp>_A/folder<timestamp>_B
String folderB = "folder" + System.currentTimeMillis() + "_B";
NodeRef folderB_Ref = repoService.createFolder(folderA_Ref, folderB);
// /Company Home/User Homes/user<timestamp>/folder<timestamp>_A/folder<timestamp>_B/folder<timestamp>_C
String folderC = "folder" + System.currentTimeMillis() + "_C";
NodeRef folderC_Ref = repoService.createFolder(folderB_Ref, folderC);
//...nodes/nodeId?select=pathInfo
Map<String, String> params = Collections.singletonMap("select", "path");
response = getSingle(NodesEntityResource.class, user1, folderC_Ref.getId(), params, 200);
node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class);
PathInfo pathInfo = node.getPath();
assertNotNull(pathInfo);
assertTrue(pathInfo.getIsComplete());
assertNotNull(pathInfo.getName());
// the pathInfo should only include the parents (not the requested node)
assertFalse(pathInfo.getName().endsWith(folderC));
assertTrue(pathInfo.getName().startsWith("/Company Home"));
List<ElementInfo> pathElements = pathInfo.getElements();
assertEquals(5, pathElements.size());
assertEquals("Company Home", pathElements.get(0).getName());
assertNotNull(pathElements.get(0).getId());
assertEquals("User Homes", pathElements.get(1).getName());
assertNotNull(pathElements.get(1).getId());
assertEquals(user1, pathElements.get(2).getName());
assertNotNull(pathElements.get(2).getId());
assertEquals(folderA, pathElements.get(3).getName());
assertNotNull(pathElements.get(3).getId());
assertEquals(folderB, pathElements.get(4).getName());
assertNotNull(pathElements.get(4).getId());
}
@Test
public void testGetNodeWithKnownAlias() throws Exception
{
final String rootNodeAlias = "-root-";
HttpResponse response = getSingle(NodesEntityResource.class, user1, rootNodeAlias, null, 200);
Node node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class);
assertEquals("Company Home", node.getName());
assertNotNull(node.getNodeRef());
PathInfo pathInfo = node.getPath();
// empty JSON object ("path":{})
assertNotNull(pathInfo);
// as this is the root node, there will be no name or path elements.
assertNull(pathInfo.getIsComplete());
assertNull(pathInfo.getName());
assertNull(pathInfo.getElements());
// unknown alias
getSingle(NodesEntityResource.class, user1, "testSomeUndefinedAlias", null, 404);
final String userNodeAlias = "-my-";
response = getSingle(NodesEntityResource.class, user1, userNodeAlias, null, 200);
node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class);
NodeRef myFilesNodeRef = node.getNodeRef();
assertNotNull(myFilesNodeRef);
assertEquals(user1, node.getName());
assertTrue(node.getIsFolder());
pathInfo = node.getPath();
assertNotNull(pathInfo);
assertTrue(pathInfo.getIsComplete());
//Delete user1's home
AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser();
repoService.getNodeService().deleteNode(myFilesNodeRef);
AuthenticationUtil.setFullyAuthenticatedUser(user1);
getSingle(NodesEntityResource.class, user1, userNodeAlias, null, 404); // Not found
}
private String getChildrenUrl(NodeRef nodeRef)
{
return "nodes/" + nodeRef.getId() + "/children";
}
@Override
public String getScope()
{
return "public";
}
}

View File

@@ -23,6 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.tests.client;
import java.io.IOException;
@@ -66,13 +67,11 @@ import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
* A http client for talking to the rest apis.
*
* The caller can pass in a rest api implementation class to the http method (get, post, put, delete supported)
* A http client for talking to the rest apis. The caller can pass in a rest api
* implementation class to the http method (get, post, put, delete supported)
* and the url will be generated.
*
* @author steveglover
*
*/
public class PublicApiHttpClient
{
@@ -80,7 +79,7 @@ public class PublicApiHttpClient
private static final String OLD_BASE_URL = "{0}://{1}:{2}{3}{4}{5}/api/";
private static final String INDEX_URL = "{0}://{1}:{2}{3}{4}";
private static final String BASE_URL = "{0}://{1}:{2}{3}{4}{5}/{6}/{7}/versions/1";
private static final String BASE_URL = "{0}://{1}:{2}{3}{4}{5}/{6}/{7}/versions/{8}/";
private static final String PUBLICAPI_CMIS_SERVICE_URL = "{0}://{1}:{2}{3}{4}cmis/versions/{5}/{6}";
private static final String PUBLICAPI_CMIS_URL = "{0}://{1}:{2}{3}{4}{5}/{6}/cmis/versions/{7}/{8}";
private static final String PUBLICAPI_CMIS_URL_SUFFIX = "{0}/{1}/cmis/versions/{2}/{3}";
@@ -156,8 +155,8 @@ public class PublicApiHttpClient
StringBuilder url = new StringBuilder();
if (networkId == null)
{
url.append(MessageFormat.format(PUBLICAPI_CMIS_SERVICE_URL, new Object[] { scheme, host, String.valueOf(port), contextPath,
servletName, version, binding.toString().toLowerCase()}));
url.append(MessageFormat.format(PUBLICAPI_CMIS_SERVICE_URL,
new Object[] { scheme, host, String.valueOf(port), contextPath, servletName, version, binding.toString().toLowerCase() }));
}
else
{
@@ -178,8 +177,7 @@ public class PublicApiHttpClient
{
StringBuilder url = new StringBuilder();
url.append(MessageFormat.format(PUBLICAPI_CMIS_URL_SUFFIX,
new Object[] {networkId, "public", version, binding.toString().toLowerCase()}));
url.append(MessageFormat.format(PUBLICAPI_CMIS_URL_SUFFIX, new Object[] { networkId, "public", version, binding.toString().toLowerCase() }));
if (operation != null)
{
@@ -287,7 +285,8 @@ public class PublicApiHttpClient
return submitRequest(req, rq);
}
public HttpResponse get(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId, Map<String, String> params) throws IOException
public HttpResponse get(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId,
Map<String, String> params) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(c, rq.getNetworkId(), entityId, relationshipEntityId, params);
String url = endpoint.getUrl();
@@ -296,18 +295,34 @@ public class PublicApiHttpClient
return submitRequest(req, rq);
}
public HttpResponse get(final RequestContext rq, String scope, final String entityCollectionName, final Object entityId, final String relationCollectionName, final Object relationshipEntityId, Map<String, String> params) throws IOException
public HttpResponse get(final RequestContext rq, String scope, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId, Map<String, String> params) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, params);
return get(rq, scope, 1, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, params);
}
public HttpResponse get(final RequestContext rq, final String scope, final int version, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId, Map<String, String> params) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, version, entityCollectionName, entityId, relationCollectionName,
relationshipEntityId, params);
String url = endpoint.getUrl();
GetMethod req = new GetMethod(url);
return submitRequest(req, rq);
}
public HttpResponse get(final RequestContext rq, String scope, String password, final String entityCollectionName, final Object entityId, final String relationCollectionName, final Object relationshipEntityId, Map<String, String> params) throws IOException
public HttpResponse get(final RequestContext rq, String scope, String password, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId, Map<String, String> params) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, params);
return get(rq, scope, 1, password, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, params);
}
public HttpResponse get(final RequestContext rq, final String scope, final int version, final String password, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId, Map<String, String> params) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, version, entityCollectionName, entityId, relationCollectionName,
relationshipEntityId, params);
String url = endpoint.getUrl();
GetMethod req = new GetMethod(url);
@@ -332,7 +347,8 @@ public class PublicApiHttpClient
return submitRequest(req, rq);
}
public HttpResponse post(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId, final String body) throws IOException
public HttpResponse post(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId, final String body)
throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(c, rq.getNetworkId(), entityId, relationshipEntityId, null);
String url = endpoint.getUrl();
@@ -404,7 +420,8 @@ public class PublicApiHttpClient
return submitRequest(req, rq);
}
public HttpResponse get(final RequestContext rq, Binding cmisBinding, String version, String cmisOperation, Map<String, String> parameters) throws IOException
public HttpResponse get(final RequestContext rq, Binding cmisBinding, String version, String cmisOperation, Map<String, String> parameters)
throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), cmisBinding, version, cmisOperation, parameters);
String url = endpoint.getUrl();
@@ -467,7 +484,13 @@ public class PublicApiHttpClient
public HttpResponse post(final RequestContext rq, final String scope, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId, final String body, String contentType) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, entityCollectionName, entityId, relationCollectionName,
return post(rq, scope, 1, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, body, contentType);
}
public HttpResponse post(final RequestContext rq, final String scope, final int version, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId, final String body, String contentType) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, version, entityCollectionName, entityId, relationCollectionName,
relationshipEntityId, null);
String url = endpoint.getUrl();
@@ -484,7 +507,6 @@ public class PublicApiHttpClient
return submitRequest(req, rq);
}
public HttpResponse delete(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(c, rq.getNetworkId(), entityId, relationshipEntityId, null);
@@ -494,16 +516,25 @@ public class PublicApiHttpClient
return submitRequest(req, rq);
}
public HttpResponse delete(final RequestContext rq, final String scope, final String entityCollectionName, final Object entityId, final String relationCollectionName, final Object relationshipEntityId) throws IOException
public HttpResponse delete(final RequestContext rq, final String scope, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, null);
return delete(rq, scope, 1, entityCollectionName, entityId, relationCollectionName, relationshipEntityId);
}
public HttpResponse delete(final RequestContext rq, final String scope, final int version, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, version, entityCollectionName, entityId, relationCollectionName,
relationshipEntityId, null);
String url = endpoint.getUrl();
DeleteMethod req = new DeleteMethod(url);
return submitRequest(req, rq);
}
public HttpResponse put(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId, final String body) throws IOException
public HttpResponse put(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId, final String body)
throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(c, rq.getNetworkId(), entityId, relationshipEntityId, null);
String url = endpoint.getUrl();
@@ -517,9 +548,17 @@ public class PublicApiHttpClient
return submitRequest(req, rq);
}
public HttpResponse put(final RequestContext rq, final String scope, final String entityCollectionName, final Object entityId, final String relationCollectionName, final Object relationshipEntityId, final String body, final Map<String, String> params) throws IOException
public HttpResponse put(final RequestContext rq, final String scope, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId, final String body, final Map<String, String> params) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, params);
return put(rq, scope, 1, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, body, params);
}
public HttpResponse put(final RequestContext rq, final String scope, final int version, final String entityCollectionName, final Object entityId,
final String relationCollectionName, final Object relationshipEntityId, final String body, Map<String, String> params) throws IOException
{
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, version, entityCollectionName, entityId, relationCollectionName,
relationshipEntityId, params);
String url = endpoint.getUrl();
PutMethod req = new PutMethod(url);
@@ -532,8 +571,8 @@ public class PublicApiHttpClient
}
/*
* Encapsulates information relating to a rest api end point, generating and encoding urls
* based on the rest api implementation class.
* Encapsulates information relating to a rest api end point, generating and
* encoding urls based on the rest api implementation class.
*/
private class RestApiEndpoint
{
@@ -541,7 +580,8 @@ public class PublicApiHttpClient
RestApiEndpoint(String url, Map<String, String> params) throws IOException
{
StringBuilder sb = new StringBuilder(MessageFormat.format(INDEX_URL, new Object[] {scheme, host, String.valueOf(port), contextPath, servletName}));
StringBuilder sb = new StringBuilder(
MessageFormat.format(INDEX_URL, new Object[] { scheme, host, String.valueOf(port), contextPath, servletName }));
if (url != null)
{
sb.append(url);
@@ -554,8 +594,8 @@ public class PublicApiHttpClient
RestApiEndpoint(String tenantDomain, String url, Map<String, String> params) throws IOException
{
StringBuilder sb = new StringBuilder(MessageFormat.format(OLD_BASE_URL, new Object[] {scheme, host, String.valueOf(port), contextPath, servletName,
tenantDomain == null ? TenantUtil.DEFAULT_TENANT : tenantDomain }));
StringBuilder sb = new StringBuilder(MessageFormat.format(OLD_BASE_URL, new Object[] { scheme, host, String.valueOf(port), contextPath,
servletName, tenantDomain == null ? TenantUtil.DEFAULT_TENANT : tenantDomain }));
sb.append("/");
sb.append(url);
@@ -564,16 +604,18 @@ public class PublicApiHttpClient
this.url = sb.toString();
}
RestApiEndpoint(Class<?> resourceClass, String tenantDomain, Object collectionEntityId, Object relationEntityId, Map<String, String> params) throws IOException
RestApiEndpoint(Class<?> resourceClass, String tenantDomain, Object collectionEntityId, Object relationEntityId, Map<String, String> params)
throws IOException
{
StringBuilder sb = new StringBuilder();
Api api = ResourceInspector.inspectApi(resourceClass);
SCOPE scope = api.getScope();
int version = api.getVersion();
Pair<String, String> relationshipCollectionInfo = getRelationCollectionInfo(resourceClass);
sb.append(MessageFormat.format(BASE_URL, new Object[] { scheme, host, String.valueOf(port), contextPath, servletName,
tenantDomain == null ? TenantUtil.DEFAULT_TENANT : tenantDomain, scope.toString(), apiName}));
tenantDomain == null ? TenantUtil.DEFAULT_TENANT : tenantDomain, scope.toString(), apiName, version }));
if (relationshipCollectionInfo != null)
{
@@ -642,7 +684,8 @@ public class PublicApiHttpClient
this.url = sb.toString();
}
RestApiEndpoint(String tenantDomain, String scope, String collectionName, Object collectionEntityId, String relationName, Object relationEntityId, Map<String, String> params) throws IOException
RestApiEndpoint(String tenantDomain, String scope, int version, String collectionName, Object collectionEntityId, String relationName,
Object relationEntityId, Map<String, String> params) throws IOException
{
StringBuilder sb = new StringBuilder();
@@ -650,27 +693,29 @@ public class PublicApiHttpClient
{
tenantDomain = TenantUtil.DEFAULT_TENANT;
}
sb.append(MessageFormat.format(BASE_URL, new Object[] {scheme, host, String.valueOf(port), contextPath, servletName,
tenantDomain, scope, apiName}));
sb.append(MessageFormat.format(BASE_URL,
new Object[] { scheme, host, String.valueOf(port), contextPath, servletName, tenantDomain, scope, apiName, version }));
if (collectionName != null)
{
sb.append("/");
sb.append(collectionName);
if (collectionEntityId != null)
{
sb.append("/");
sb.append('/');
sb.append(collectionEntityId);
}
}
if (relationName != null)
{
sb.append("/");
if (!sb.toString().endsWith("/"))
{
sb.append('/');
}
sb.append(relationName);
if (relationEntityId != null)
{
sb.append("/");
sb.append('/');
sb.append(relationEntityId);
}
}
@@ -680,6 +725,12 @@ public class PublicApiHttpClient
this.url = sb.toString();
}
RestApiEndpoint(String tenantDomain, String scope, String collectionName, Object collectionEntityId, String relationName,
Object relationEntityId, Map<String, String> params) throws IOException
{
this(tenantDomain, scope, 1, collectionName, collectionEntityId, relationName, relationEntityId, params);
}
private void addParams(StringBuilder sb, Map<String, String> params) throws UnsupportedEncodingException
{
if (params != null && params.size() > 0)

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.rest.api.tests.util;
import static org.junit.Assert.assertNotNull;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
/**
* @author Jamal Kaabi-Mofrad
*/
public class JacksonUtil
{
private JacksonHelper jsonHelper;
public JacksonUtil(JacksonHelper jsonHelper)
{
this.jsonHelper = jsonHelper;
}
public <T> List<T> parseEntries(JSONObject jsonObject, Class<T> clazz) throws IOException
{
assertNotNull(jsonObject);
assertNotNull(clazz);
List<T> models = new ArrayList<>();
JSONObject jsonList = (JSONObject) jsonObject.get("list");
assertNotNull(jsonList);
JSONArray jsonEntries = (JSONArray) jsonList.get("entries");
assertNotNull(jsonEntries);
for (Object entry : jsonEntries)
{
JSONObject jsonEntry = (JSONObject) entry;
T pojoModel = parseEntry(jsonEntry, clazz);
models.add(pojoModel);
}
return models;
}
public <T> T parseEntry(JSONObject jsonObject, Class<T> clazz) throws IOException
{
assertNotNull(jsonObject);
assertNotNull(clazz);
JSONObject entry = (JSONObject) jsonObject.get("entry");
T pojoModel = jsonHelper.construct(new StringReader(entry.toJSONString()), clazz);
assertNotNull(pojoModel);
return pojoModel;
}
}