mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-14 17:58:59 +00:00
Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2)
119504 jkaabimofrad: RA-637, SFS-260: Added multipart upload REST API. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@126357 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -11,4 +11,5 @@ framework.exception.PermissionDenied=Permission was denied
|
|||||||
framework.exception.StaleEntity=Attempt to update a stale entity
|
framework.exception.StaleEntity=Attempt to update a stale entity
|
||||||
framework.exception.UnsupportedResourceOperation=The operation is unsupported
|
framework.exception.UnsupportedResourceOperation=The operation is unsupported
|
||||||
framework.exception.DeletedResource=In this version of the API resource {0} has been deleted
|
framework.exception.DeletedResource=In this version of the API resource {0} has been deleted
|
||||||
|
framework.exception.RequestEntityTooLarge=Request entity too large
|
||||||
|
|
||||||
|
@@ -137,7 +137,8 @@
|
|||||||
<entry key="org.alfresco.rest.framework.core.exceptions.PermissionDeniedException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_FORBIDDEN}" />
|
<entry key="org.alfresco.rest.framework.core.exceptions.PermissionDeniedException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_FORBIDDEN}" />
|
||||||
<entry key="org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_METHOD_NOT_ALLOWED}" />
|
<entry key="org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_METHOD_NOT_ALLOWED}" />
|
||||||
<entry key="org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_CONFLICT}" />
|
<entry key="org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_CONFLICT}" />
|
||||||
<entry key="org.alfresco.rest.framework.core.exceptions.StaleEntityException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_CONFLICT}" />
|
<entry key="org.alfresco.rest.framework.core.exceptions.StaleEntityException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_CONFLICT}" />
|
||||||
|
<entry key="org.alfresco.rest.framework.core.exceptions.RequestEntityTooLargeException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_REQUEST_ENTITY_TOO_LARGE}" />
|
||||||
</map>
|
</map>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
@@ -31,6 +31,7 @@ import org.alfresco.rest.framework.resource.parameters.Parameters;
|
|||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.springframework.extensions.webscripts.servlet.FormData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author steveglover
|
* @author steveglover
|
||||||
@@ -38,11 +39,11 @@ import org.alfresco.service.namespace.QName;
|
|||||||
*/
|
*/
|
||||||
public interface Nodes
|
public interface Nodes
|
||||||
{
|
{
|
||||||
NodeRef validateNode(StoreRef storeRef, String nodeId);
|
NodeRef validateNode(StoreRef storeRef, String nodeId);
|
||||||
NodeRef validateNode(String nodeId);
|
NodeRef validateNode(String nodeId);
|
||||||
NodeRef validateNode(NodeRef nodeRef);
|
NodeRef validateNode(NodeRef nodeRef);
|
||||||
boolean nodeMatches(NodeRef nodeRef, Set<QName> expectedTypes, Set<QName> excludedTypes);
|
boolean nodeMatches(NodeRef nodeRef, Set<QName> expectedTypes, Set<QName> excludedTypes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the node representation for the given node.
|
* Get the node representation for the given node.
|
||||||
* @param nodeId String
|
* @param nodeId String
|
||||||
@@ -115,4 +116,14 @@ public interface Nodes
|
|||||||
|
|
||||||
// TODO update REST fwk - to optionally support return of json
|
// TODO update REST fwk - to optionally support return of json
|
||||||
void updateContent(String fileNodeId, BasicContentInfo contentInfo, InputStream stream, Parameters parameters);
|
void updateContent(String fileNodeId, BasicContentInfo contentInfo, InputStream stream, Parameters parameters);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads file content and meta-data into the repository.
|
||||||
|
*
|
||||||
|
* @param parentFolderNodeId String id of parent folder node or well-known alias, eg. "-root-" or "-my-"
|
||||||
|
* @param formData the {@link FormData}
|
||||||
|
* @param parameters the {@link Parameters} object to get the parameters passed into the request
|
||||||
|
* @return {@code Node} if successful
|
||||||
|
*/
|
||||||
|
Node upload(String parentFolderNodeId, FormData formData, Parameters parameters);
|
||||||
}
|
}
|
||||||
|
@@ -21,8 +21,10 @@ package org.alfresco.rest.api.impl;
|
|||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.query.PagingRequest;
|
import org.alfresco.query.PagingRequest;
|
||||||
import org.alfresco.query.PagingResults;
|
import org.alfresco.query.PagingResults;
|
||||||
|
import org.alfresco.repo.content.ContentLimitViolationException;
|
||||||
import org.alfresco.repo.model.Repository;
|
import org.alfresco.repo.model.Repository;
|
||||||
import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery;
|
import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery;
|
||||||
|
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
||||||
import org.alfresco.rest.antlr.WhereClauseParser;
|
import org.alfresco.rest.antlr.WhereClauseParser;
|
||||||
import org.alfresco.rest.api.Nodes;
|
import org.alfresco.rest.api.Nodes;
|
||||||
import org.alfresco.rest.api.model.Document;
|
import org.alfresco.rest.api.model.Document;
|
||||||
@@ -31,8 +33,12 @@ import org.alfresco.rest.api.model.Node;
|
|||||||
import org.alfresco.rest.api.model.PathInfo;
|
import org.alfresco.rest.api.model.PathInfo;
|
||||||
import org.alfresco.rest.api.model.PathInfo.ElementInfo;
|
import org.alfresco.rest.api.model.PathInfo.ElementInfo;
|
||||||
import org.alfresco.rest.api.model.UserInfo;
|
import org.alfresco.rest.api.model.UserInfo;
|
||||||
|
import org.alfresco.rest.framework.core.exceptions.ApiException;
|
||||||
|
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
|
||||||
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
|
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
|
||||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
||||||
|
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
|
||||||
|
import org.alfresco.rest.framework.core.exceptions.RequestEntityTooLargeException;
|
||||||
import org.alfresco.rest.framework.resource.content.BasicContentInfo;
|
import org.alfresco.rest.framework.resource.content.BasicContentInfo;
|
||||||
import org.alfresco.rest.framework.resource.content.BinaryResource;
|
import org.alfresco.rest.framework.resource.content.BinaryResource;
|
||||||
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
|
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
|
||||||
@@ -44,14 +50,19 @@ import org.alfresco.rest.framework.resource.parameters.where.Query;
|
|||||||
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
|
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
|
||||||
import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
|
import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
|
||||||
import org.alfresco.service.ServiceRegistry;
|
import org.alfresco.service.ServiceRegistry;
|
||||||
|
import org.alfresco.service.cmr.action.Action;
|
||||||
|
import org.alfresco.service.cmr.action.ActionDefinition;
|
||||||
|
import org.alfresco.service.cmr.action.ActionService;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.model.FileFolderService;
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
import org.alfresco.service.cmr.model.FileInfo;
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
import org.alfresco.service.cmr.model.FileNotFoundException;
|
import org.alfresco.service.cmr.model.FileNotFoundException;
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
import org.alfresco.service.cmr.repository.ContentData;
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||||
|
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.repository.Path;
|
import org.alfresco.service.cmr.repository.Path;
|
||||||
@@ -59,13 +70,23 @@ import org.alfresco.service.cmr.repository.Path.Element;
|
|||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.cmr.security.AccessStatus;
|
import org.alfresco.service.cmr.security.AccessStatus;
|
||||||
import org.alfresco.service.cmr.security.PermissionService;
|
import org.alfresco.service.cmr.security.PermissionService;
|
||||||
|
import org.alfresco.service.cmr.usage.ContentQuotaException;
|
||||||
|
import org.alfresco.service.cmr.version.VersionService;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.util.Pair;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.extensions.surf.util.Content;
|
||||||
|
import org.springframework.extensions.webscripts.servlet.FormData;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -88,6 +109,8 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class NodesImpl implements Nodes
|
public class NodesImpl implements Nodes
|
||||||
{
|
{
|
||||||
|
private static final Log logger = LogFactory.getLog(NodesImpl.class);
|
||||||
|
|
||||||
private static enum Type
|
private static enum Type
|
||||||
{
|
{
|
||||||
// Note: ordered
|
// Note: ordered
|
||||||
@@ -107,6 +130,10 @@ public class NodesImpl implements Nodes
|
|||||||
private FileFolderService fileFolderService;
|
private FileFolderService fileFolderService;
|
||||||
private NamespaceService namespaceService;
|
private NamespaceService namespaceService;
|
||||||
private PermissionService permissionService;
|
private PermissionService permissionService;
|
||||||
|
private MimetypeService mimetypeService;
|
||||||
|
private ContentService contentService;
|
||||||
|
private ActionService actionService;
|
||||||
|
private VersionService versionService;
|
||||||
private Repository repositoryHelper;
|
private Repository repositoryHelper;
|
||||||
private ServiceRegistry sr;
|
private ServiceRegistry sr;
|
||||||
private Set<String> defaultIgnoreTypes;
|
private Set<String> defaultIgnoreTypes;
|
||||||
@@ -114,6 +141,16 @@ public class NodesImpl implements Nodes
|
|||||||
|
|
||||||
public void init()
|
public void init()
|
||||||
{
|
{
|
||||||
|
this.namespaceService = sr.getNamespaceService();
|
||||||
|
this.fileFolderService = sr.getFileFolderService();
|
||||||
|
this.nodeService = sr.getNodeService();
|
||||||
|
this.permissionService = sr.getPermissionService();
|
||||||
|
this.dictionaryService = sr.getDictionaryService();
|
||||||
|
this.mimetypeService = sr.getMimetypeService();
|
||||||
|
this.contentService = sr.getContentService();
|
||||||
|
this.actionService = sr.getActionService();
|
||||||
|
this.versionService = sr.getVersionService();
|
||||||
|
|
||||||
if (defaultIgnoreTypes != null)
|
if (defaultIgnoreTypes != null)
|
||||||
{
|
{
|
||||||
ignoreTypeQNames = new HashSet<>(defaultIgnoreTypes.size());
|
ignoreTypeQNames = new HashSet<>(defaultIgnoreTypes.size());
|
||||||
@@ -126,12 +163,6 @@ public class NodesImpl implements Nodes
|
|||||||
|
|
||||||
public void setServiceRegistry(ServiceRegistry sr) {
|
public void setServiceRegistry(ServiceRegistry sr) {
|
||||||
this.sr = sr;
|
this.sr = sr;
|
||||||
|
|
||||||
this.namespaceService = sr.getNamespaceService();
|
|
||||||
this.fileFolderService = sr.getFileFolderService();
|
|
||||||
this.nodeService = sr.getNodeService();
|
|
||||||
this.permissionService = sr.getPermissionService();
|
|
||||||
this.dictionaryService = sr.getDictionaryService();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRepositoryHelper(Repository repositoryHelper)
|
public void setRepositoryHelper(Repository repositoryHelper)
|
||||||
@@ -785,15 +816,15 @@ public class NodesImpl implements Nodes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO should we able to specify content properties (eg. mimeType ... or use extension for now, or encoding)
|
// TODO should we able to specify content properties (eg. mimeType ... or use extension for now, or encoding)
|
||||||
public Node createNode(String parentFolderNodeId, Node nodeInfo, Parameters parameters)
|
public Node createNode(String parentFolderNodeId, Node nodeInfo, Parameters parameters)
|
||||||
{
|
{
|
||||||
// check that requested parent node exists and it's type is a (sub-)type of folder
|
// check that requested parent node exists and it's type is a (sub-)type of folder
|
||||||
final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null);
|
final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null);
|
||||||
|
|
||||||
if (! nodeMatches(parentNodeRef, Collections.singleton(ContentModel.TYPE_FOLDER), null))
|
if (! nodeMatches(parentNodeRef, Collections.singleton(ContentModel.TYPE_FOLDER), null))
|
||||||
{
|
{
|
||||||
throw new InvalidArgumentException("NodeId of folder is expected: "+parentNodeRef);
|
throw new InvalidArgumentException("NodeId of folder is expected: "+parentNodeRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
String nodeName = nodeInfo.getName();
|
String nodeName = nodeInfo.getName();
|
||||||
if ((nodeName == null) || nodeName.isEmpty())
|
if ((nodeName == null) || nodeName.isEmpty())
|
||||||
@@ -842,7 +873,7 @@ public class NodesImpl implements Nodes
|
|||||||
}
|
}
|
||||||
|
|
||||||
return getFolderOrDocument(nodeRef.getId(), parameters);
|
return getFolderOrDocument(nodeRef.getId(), parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Node updateNode(String nodeId, Node nodeInfo, Parameters parameters)
|
public Node updateNode(String nodeId, Node nodeInfo, Parameters parameters)
|
||||||
{
|
{
|
||||||
@@ -926,6 +957,259 @@ public class NodesImpl implements Nodes
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node upload(String parentFolderNodeId, FormData formData, Parameters parameters)
|
||||||
|
{
|
||||||
|
if (formData == null || !formData.getIsMultiPart())
|
||||||
|
{
|
||||||
|
throw new InvalidArgumentException("The request content-type is not multipart");
|
||||||
|
}
|
||||||
|
|
||||||
|
final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null);
|
||||||
|
if (Type.DOCUMENT == getType(parentNodeRef))
|
||||||
|
{
|
||||||
|
throw new InvalidArgumentException(parentFolderNodeId + " is not a folder.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileName = null;
|
||||||
|
Content content = null;
|
||||||
|
boolean overwrite = false; // If a fileName clashes for a versionable file
|
||||||
|
|
||||||
|
for (FormData.FormField field : formData.getFields())
|
||||||
|
{
|
||||||
|
switch (field.getName().toLowerCase())
|
||||||
|
{
|
||||||
|
case "filename":
|
||||||
|
fileName = getStringOrNull(field.getValue());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "filedata":
|
||||||
|
if (field.getIsFile())
|
||||||
|
{
|
||||||
|
fileName = fileName != null ? fileName : field.getFilename();
|
||||||
|
content = field.getContent();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "overwrite":
|
||||||
|
overwrite = Boolean.valueOf(field.getValue());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// MNT-7213 When alf_data runs out of disk space, Share uploads
|
||||||
|
// result in a success message, but the files do not appear.
|
||||||
|
if (formData.getFields().length == 0)
|
||||||
|
{
|
||||||
|
throw new ConstraintViolatedException(" No disk space available");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure mandatory file attributes have been located. Need either
|
||||||
|
// destination, or site + container or updateNodeRef
|
||||||
|
if ((fileName == null || content == null))
|
||||||
|
{
|
||||||
|
throw new InvalidArgumentException("Required parameters are missing");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Existing file handling
|
||||||
|
*/
|
||||||
|
NodeRef existingFile = nodeService.getChildByName(parentNodeRef, ContentModel.ASSOC_CONTAINS, fileName);
|
||||||
|
if (existingFile != null)
|
||||||
|
{
|
||||||
|
// File already exists, decide what to do
|
||||||
|
if (overwrite && nodeService.hasAspect(existingFile, ContentModel.ASPECT_VERSIONABLE))
|
||||||
|
{
|
||||||
|
// Upload component was configured to overwrite files if name clashes
|
||||||
|
write(existingFile, content, fileName, false, true);
|
||||||
|
|
||||||
|
// Extract the metadata (The overwrite policy controls
|
||||||
|
// which if any parts of the document's properties are updated from this)
|
||||||
|
extractMetadata(existingFile);
|
||||||
|
|
||||||
|
// Do not clean formData temp files to
|
||||||
|
// allow for retries. Temp files will be deleted later
|
||||||
|
// when GC call DiskFileItem#finalize() method or by temp file cleaner.
|
||||||
|
return createUploadResponse(parentNodeRef, existingFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ConstraintViolatedException(fileName + " already exists.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new file.
|
||||||
|
return createNewFile(parentNodeRef, fileName, content);
|
||||||
|
|
||||||
|
// Do not clean formData temp files to allow for retries.
|
||||||
|
// Temp files will be deleted later when GC call DiskFileItem#finalize() method or by temp file cleaner.
|
||||||
|
}
|
||||||
|
catch (ApiException apiEx)
|
||||||
|
{
|
||||||
|
// As this is an public API fwk exception, there is no need to convert it, so just throw it.
|
||||||
|
throw apiEx;
|
||||||
|
}
|
||||||
|
catch (AccessDeniedException ade)
|
||||||
|
{
|
||||||
|
throw new PermissionDeniedException();
|
||||||
|
}
|
||||||
|
catch (ContentQuotaException cqe)
|
||||||
|
{
|
||||||
|
throw new RequestEntityTooLargeException();
|
||||||
|
}
|
||||||
|
catch (ContentLimitViolationException clv)
|
||||||
|
{
|
||||||
|
throw new ConstraintViolatedException();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: Do not clean formData temp files to allow for retries. It's
|
||||||
|
* possible for a temp file to remain if max retry attempts are
|
||||||
|
* made, but this is rare, so leave to usual temp file cleanup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
throw new ApiException("Unexpected error occurred during upload of new content.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to create a new node and writes its content to the repository.
|
||||||
|
*/
|
||||||
|
private Node createNewFile(NodeRef parentNodeRef, String fileName, Content content)
|
||||||
|
{
|
||||||
|
FileInfo fileInfo = fileFolderService.create(parentNodeRef, fileName, ContentModel.TYPE_CONTENT);
|
||||||
|
NodeRef newFile = fileInfo.getNodeRef();
|
||||||
|
|
||||||
|
// Write content
|
||||||
|
write(newFile, content, fileName, false, true);
|
||||||
|
|
||||||
|
// Ensure the file is versionable (autoVersion = true, autoVersionProps = false)
|
||||||
|
ensureVersioningEnabled(newFile, true, false);
|
||||||
|
|
||||||
|
// Extract the metadata
|
||||||
|
extractMetadata(newFile);
|
||||||
|
|
||||||
|
// Create the response
|
||||||
|
return createUploadResponse(parentNodeRef, newFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node createUploadResponse(NodeRef parentNodeRef, NodeRef newFileNodeRef)
|
||||||
|
{
|
||||||
|
return getFolderOrDocument(newFileNodeRef, parentNodeRef, ContentModel.TYPE_CONTENT, Collections.<String>emptyList(), true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStringOrNull(String value)
|
||||||
|
{
|
||||||
|
if (StringUtils.isNotEmpty(value))
|
||||||
|
{
|
||||||
|
return value.equalsIgnoreCase("null") ? null : value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the content to the repository.
|
||||||
|
*
|
||||||
|
* @param nodeRef the reference to the node having a content property
|
||||||
|
* @param content the content
|
||||||
|
* @param fileName the uploaded file name
|
||||||
|
* @param applyMimeType If true, apply the mimeType from the Content object,
|
||||||
|
* else leave the original mimeType
|
||||||
|
* @param guessEncoding If true, guess the encoding from the underlying
|
||||||
|
* input stream, else use encoding set in the Content object as supplied
|
||||||
|
*/
|
||||||
|
protected void write(NodeRef nodeRef, Content content, String fileName, boolean applyMimeType, boolean guessEncoding)
|
||||||
|
{
|
||||||
|
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
|
||||||
|
InputStream is;
|
||||||
|
String mimeType = content.getMimetype();
|
||||||
|
if (!applyMimeType)
|
||||||
|
{
|
||||||
|
ContentData existingContentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
|
||||||
|
if (existingContentData != null)
|
||||||
|
{
|
||||||
|
mimeType = existingContentData.getMimetype();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mimeType = mimetypeService.guessMimetype(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (guessEncoding)
|
||||||
|
{
|
||||||
|
is = new BufferedInputStream(content.getInputStream());
|
||||||
|
is.mark(1024);
|
||||||
|
|
||||||
|
writer.setEncoding(guessEncoding(is, mimeType));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
is.reset();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.error("Failed to reset input stream", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.setEncoding(content.getEncoding());
|
||||||
|
is = content.getInputStream();
|
||||||
|
}
|
||||||
|
writer.setMimetype(mimeType);
|
||||||
|
writer.putContent(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures the given node has the {@code cm:versionable} aspect applied to it, and
|
||||||
|
* that it has the initial version in the version store.
|
||||||
|
*
|
||||||
|
* @param nodeRef the reference to the node to be checked
|
||||||
|
* @param autoVersion If the {@code cm:versionable} aspect is applied, should auto
|
||||||
|
* versioning be requested?
|
||||||
|
* @param autoVersionProps If the {@code cm:versionable} aspect is applied, should
|
||||||
|
* auto versioning of properties be requested?
|
||||||
|
*/
|
||||||
|
protected void ensureVersioningEnabled(NodeRef nodeRef, boolean autoVersion, boolean autoVersionProps)
|
||||||
|
{
|
||||||
|
Map<QName, Serializable> props = new HashMap<>(2);
|
||||||
|
props.put(ContentModel.PROP_AUTO_VERSION, autoVersion);
|
||||||
|
props.put(ContentModel.PROP_AUTO_VERSION_PROPS, autoVersionProps);
|
||||||
|
|
||||||
|
versionService.ensureVersioningEnabled(nodeRef, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guesses the character encoding of the given inputStream.
|
||||||
|
*/
|
||||||
|
protected String guessEncoding(InputStream in, String mimeType)
|
||||||
|
{
|
||||||
|
String encoding = "UTF-8";
|
||||||
|
|
||||||
|
if (in != null)
|
||||||
|
{
|
||||||
|
Charset charset = mimetypeService.getContentCharsetFinder().getCharset(in, mimeType);
|
||||||
|
encoding = charset.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the given node metadata asynchronously.
|
||||||
|
*/
|
||||||
|
private void extractMetadata(NodeRef nodeRef)
|
||||||
|
{
|
||||||
|
final String actionName = "extract-metadata";
|
||||||
|
ActionDefinition actionDef = actionService.getActionDefinition(actionName);
|
||||||
|
if (actionDef != null)
|
||||||
|
{
|
||||||
|
Action action = actionService.createAction(actionName);
|
||||||
|
actionService.executeAction(action, nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to create a QName from either a fully qualified or short-name QName string
|
* Helper to create a QName from either a fully qualified or short-name QName string
|
||||||
*
|
*
|
||||||
|
@@ -19,15 +19,17 @@
|
|||||||
package org.alfresco.rest.api.nodes;
|
package org.alfresco.rest.api.nodes;
|
||||||
|
|
||||||
import org.alfresco.rest.api.Nodes;
|
import org.alfresco.rest.api.Nodes;
|
||||||
import org.alfresco.rest.api.model.Folder;
|
|
||||||
import org.alfresco.rest.api.model.Node;
|
import org.alfresco.rest.api.model.Node;
|
||||||
import org.alfresco.rest.framework.WebApiDescription;
|
import org.alfresco.rest.framework.WebApiDescription;
|
||||||
|
import org.alfresco.rest.framework.WebApiParam;
|
||||||
import org.alfresco.rest.framework.resource.RelationshipResource;
|
import org.alfresco.rest.framework.resource.RelationshipResource;
|
||||||
|
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
|
||||||
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
|
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
|
||||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
|
import org.alfresco.rest.framework.resource.parameters.Parameters;
|
||||||
import org.alfresco.util.ParameterCheck;
|
import org.alfresco.util.ParameterCheck;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.extensions.webscripts.servlet.FormData;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -36,18 +38,20 @@ import java.util.List;
|
|||||||
* TODO ... work-in-progress
|
* TODO ... work-in-progress
|
||||||
*
|
*
|
||||||
* @author janv
|
* @author janv
|
||||||
|
* @author Jamal Kaabi-Mofrad
|
||||||
*/
|
*/
|
||||||
@RelationshipResource(name = "children", entityResource = NodesEntityResource.class, title = "Folder children")
|
@RelationshipResource(name = "children", entityResource = NodesEntityResource.class, title = "Folder children")
|
||||||
public class NodeChildrenRelation implements RelationshipResourceAction.Read<Node>, RelationshipResourceAction.Create<Node>, InitializingBean
|
public class NodeChildrenRelation implements RelationshipResourceAction.Read<Node>, RelationshipResourceAction.Create<Node>,
|
||||||
|
MultiPartRelationshipResourceAction.Create<Node>, InitializingBean
|
||||||
{
|
{
|
||||||
private Nodes nodes;
|
private Nodes nodes;
|
||||||
|
|
||||||
public void setNodes(Nodes nodes)
|
public void setNodes(Nodes nodes)
|
||||||
{
|
{
|
||||||
this.nodes = nodes;
|
this.nodes = nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet()
|
public void afterPropertiesSet()
|
||||||
{
|
{
|
||||||
ParameterCheck.mandatory("nodes", this.nodes);
|
ParameterCheck.mandatory("nodes", this.nodes);
|
||||||
@@ -104,4 +108,13 @@ public class NodeChildrenRelation implements RelationshipResourceAction.Read<Nod
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@WebApiDescription(title = "Upload file content and meta-data into the repository.")
|
||||||
|
@WebApiParam(name = "formData", title = "A single form data", description = "A single form data which holds FormFields.")
|
||||||
|
public Node create(String parentFolderNodeId, FormData formData, Parameters parameters)
|
||||||
|
{
|
||||||
|
return nodes.upload(parentFolderNodeId, formData, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.framework.core.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jamal Kaabi-Mofrad
|
||||||
|
*/
|
||||||
|
public class RequestEntityTooLargeException extends ApiException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 3196212354672333823L;
|
||||||
|
|
||||||
|
public static String DEFAULT_MESSAGE_ID = "framework.exception.RequestEntityTooLarge";
|
||||||
|
|
||||||
|
public RequestEntityTooLargeException()
|
||||||
|
{
|
||||||
|
super(DEFAULT_MESSAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestEntityTooLargeException(String msgId)
|
||||||
|
{
|
||||||
|
super(msgId);
|
||||||
|
}
|
||||||
|
}
|
@@ -19,18 +19,22 @@
|
|||||||
|
|
||||||
package org.alfresco.rest.api.tests;
|
package org.alfresco.rest.api.tests;
|
||||||
|
|
||||||
|
import static org.alfresco.rest.api.tests.util.RestApiUtil.parsePaging;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.model.ForumModel;
|
import org.alfresco.model.ForumModel;
|
||||||
import org.alfresco.repo.content.MimetypeMap;
|
import org.alfresco.repo.content.MimetypeMap;
|
||||||
import org.alfresco.repo.model.Repository;
|
import org.alfresco.repo.model.Repository;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
|
import org.alfresco.rest.api.model.ContentInfo;
|
||||||
import org.alfresco.rest.api.model.Document;
|
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.Node;
|
||||||
import org.alfresco.rest.api.model.PathInfo;
|
import org.alfresco.rest.api.model.PathInfo;
|
||||||
import org.alfresco.rest.api.model.PathInfo.ElementInfo;
|
import org.alfresco.rest.api.model.PathInfo.ElementInfo;
|
||||||
@@ -40,10 +44,14 @@ import org.alfresco.rest.api.tests.RepoService.TestNetwork;
|
|||||||
import org.alfresco.rest.api.tests.RepoService.TestPerson;
|
import org.alfresco.rest.api.tests.RepoService.TestPerson;
|
||||||
import org.alfresco.rest.api.tests.RepoService.TestSite;
|
import org.alfresco.rest.api.tests.RepoService.TestSite;
|
||||||
import org.alfresco.rest.api.tests.client.HttpResponse;
|
import org.alfresco.rest.api.tests.client.HttpResponse;
|
||||||
|
import org.alfresco.rest.api.tests.client.PublicApiClient;
|
||||||
import org.alfresco.rest.api.tests.client.PublicApiClient.ExpectedPaging;
|
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.PublicApiClient.Paging;
|
||||||
import org.alfresco.rest.api.tests.client.data.SiteRole;
|
import org.alfresco.rest.api.tests.client.data.SiteRole;
|
||||||
import org.alfresco.rest.api.tests.util.JacksonUtil;
|
import org.alfresco.rest.api.tests.util.JacksonUtil;
|
||||||
|
import org.alfresco.rest.api.tests.util.MultiPartBuilder;
|
||||||
|
import org.alfresco.rest.api.tests.util.MultiPartBuilder.FileData;
|
||||||
|
import org.alfresco.rest.api.tests.util.MultiPartBuilder.MultiPartRequest;
|
||||||
import org.alfresco.rest.api.tests.util.RestApiUtil;
|
import org.alfresco.rest.api.tests.util.RestApiUtil;
|
||||||
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
|
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
@@ -54,8 +62,12 @@ import org.alfresco.service.cmr.site.SiteVisibility;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.util.ResourceUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -67,14 +79,16 @@ import java.util.Set;
|
|||||||
/**
|
/**
|
||||||
* API tests for:
|
* API tests for:
|
||||||
* <ul>
|
* <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>} </li>
|
||||||
* <li> {@literal host:port/alfresco/api/{networkId}/public/alfresco/versions/1/nodes/{nodeId}/children} </li>
|
* <li> {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/children} </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Jamal Kaabi-Mofrad
|
* @author Jamal Kaabi-Mofrad
|
||||||
*/
|
*/
|
||||||
public class NodeApiTest extends AbstractBaseApiTest
|
public class NodeApiTest extends AbstractBaseApiTest
|
||||||
{
|
{
|
||||||
|
private static final String RESOURCE_PREFIX = "publicapi/upload/";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User one from network one
|
* User one from network one
|
||||||
*/
|
*/
|
||||||
@@ -145,6 +159,11 @@ public class NodeApiTest extends AbstractBaseApiTest
|
|||||||
AuthenticationUtil.clearCurrentSecurityContext();
|
AuthenticationUtil.clearCurrentSecurityContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests get document library children.
|
||||||
|
* <p>GET:</p>
|
||||||
|
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/children}
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testListDocLibChildren() throws Exception
|
public void testListDocLibChildren() throws Exception
|
||||||
{
|
{
|
||||||
@@ -246,6 +265,11 @@ public class NodeApiTest extends AbstractBaseApiTest
|
|||||||
getAll(getChildrenUrl(docLibNodeRef), userTwoN1.getId(), paging, 403);
|
getAll(getChildrenUrl(docLibNodeRef), userTwoN1.getId(), paging, 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests get user's home children.
|
||||||
|
* <p>GET:</p>
|
||||||
|
* {@literal <host>:<port>/alfresco/api/-default-/public/alfresco/versions/1/nodes/<nodeId>/children}
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testListMyFilesChildren() throws Exception
|
public void testListMyFilesChildren() throws Exception
|
||||||
{
|
{
|
||||||
@@ -317,6 +341,11 @@ public class NodeApiTest extends AbstractBaseApiTest
|
|||||||
assertEquals("doclib:1444660852296", ((List<?>) entry.getValue()).get(0));
|
assertEquals("doclib:1444660852296", ((List<?>) entry.getValue()).get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests get node with path information.
|
||||||
|
* <p>GET:</p>
|
||||||
|
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>?select=path}
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetPathElements_DocLib() throws Exception
|
public void testGetPathElements_DocLib() throws Exception
|
||||||
{
|
{
|
||||||
@@ -385,6 +414,11 @@ public class NodeApiTest extends AbstractBaseApiTest
|
|||||||
assertEquals(folderC, pathElements.get(0).getName());
|
assertEquals(folderC, pathElements.get(0).getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests get node with path information.
|
||||||
|
* <p>GET:</p>
|
||||||
|
* {@literal <host>:<port>/alfresco/api/-default-/public/alfresco/versions/1/nodes/<nodeId>?select=path}
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetPathElements_MyFiles() throws Exception
|
public void testGetPathElements_MyFiles() throws Exception
|
||||||
{
|
{
|
||||||
@@ -434,6 +468,14 @@ public class NodeApiTest extends AbstractBaseApiTest
|
|||||||
assertNotNull(pathElements.get(4).getId());
|
assertNotNull(pathElements.get(4).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests well-known aliases.
|
||||||
|
* <p>GET:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li> {@literal <host>:<port>/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-}</li>
|
||||||
|
* <li> {@literal <host>:<port>/alfresco/api/-default-/public/alfresco/versions/1/nodes/-my-} </li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetNodeWithKnownAlias() throws Exception
|
public void testGetNodeWithKnownAlias() throws Exception
|
||||||
{
|
{
|
||||||
@@ -472,9 +514,170 @@ public class NodeApiTest extends AbstractBaseApiTest
|
|||||||
getSingle(NodesEntityResource.class, user1, userNodeAlias, null, 404); // Not found
|
getSingle(NodesEntityResource.class, user1, userNodeAlias, null, 404); // Not found
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getChildrenUrl(NodeRef nodeRef)
|
/**
|
||||||
|
* Tests Multipart upload to user's home (a.k.a My Files).
|
||||||
|
* <p>POST:</p>
|
||||||
|
* {@literal <host>:<port>/alfresco/api/-default-/public/alfresco/versions/1/nodes/<nodeId>/children}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testUploadToMyFiles() throws Exception
|
||||||
{
|
{
|
||||||
return "nodes/" + nodeRef.getId() + "/children";
|
final String userNodeAlias = "-my-";
|
||||||
|
final String fileName = "quick.pdf";
|
||||||
|
final File file = getResourceFile(fileName);
|
||||||
|
|
||||||
|
Paging paging = getPaging(0, Integer.MAX_VALUE);
|
||||||
|
HttpResponse response = getAll(getChildrenUrl(userNodeAlias), user1, paging, 200);
|
||||||
|
PublicApiClient.ExpectedPaging pagingResult = parsePaging(response.getJsonResponse());
|
||||||
|
assertNotNull(paging);
|
||||||
|
final int numOfNodes = pagingResult.getCount().intValue();
|
||||||
|
|
||||||
|
MultiPartBuilder multiPartBuilder = MultiPartBuilder.create()
|
||||||
|
.setFileData(new FileData(fileName, file, MimetypeMap.MIMETYPE_PDF));
|
||||||
|
MultiPartRequest reqBody = multiPartBuilder.build();
|
||||||
|
|
||||||
|
// Try to upload
|
||||||
|
response = post(getChildrenUrl(userNodeAlias), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 201);
|
||||||
|
Document document = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class);
|
||||||
|
// Check the upload response
|
||||||
|
assertEquals(fileName, document.getName());
|
||||||
|
ContentInfo contentInfo = document.getContent();
|
||||||
|
assertNotNull(contentInfo);
|
||||||
|
assertEquals(MimetypeMap.MIMETYPE_PDF, contentInfo.getMimeType());
|
||||||
|
|
||||||
|
// Retrieve the uploaded file
|
||||||
|
response = getSingle(NodesEntityResource.class, user1, document.getNodeRef().getId(), null, 200);
|
||||||
|
document = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class);
|
||||||
|
assertEquals(fileName, document.getName());
|
||||||
|
contentInfo = document.getContent();
|
||||||
|
assertNotNull(contentInfo);
|
||||||
|
assertEquals(MimetypeMap.MIMETYPE_PDF, contentInfo.getMimeType());
|
||||||
|
|
||||||
|
// Check 'get children' is confirming the upload
|
||||||
|
response = getAll(getChildrenUrl(userNodeAlias), user1, paging, 200);
|
||||||
|
pagingResult = parsePaging(response.getJsonResponse());
|
||||||
|
assertNotNull(paging);
|
||||||
|
assertEquals(numOfNodes + 1, pagingResult.getCount().intValue());
|
||||||
|
|
||||||
|
// Upload the same file again to check the name conflicts handling
|
||||||
|
post(getChildrenUrl(userNodeAlias), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 409);
|
||||||
|
|
||||||
|
response = getAll(getChildrenUrl(userNodeAlias), user1, paging, 200);
|
||||||
|
pagingResult = parsePaging(response.getJsonResponse());
|
||||||
|
assertNotNull(paging);
|
||||||
|
assertEquals("Duplicate file name. The file shouldn't have been uploaded.", numOfNodes + 1, pagingResult.getCount().intValue());
|
||||||
|
|
||||||
|
// User2 tries to upload a new file into the user1's home folder.
|
||||||
|
response = getSingle(NodesEntityResource.class, user1, userNodeAlias, null, 200);
|
||||||
|
Folder user1Home = jacksonUtil.parseEntry(response.getJsonResponse(), Folder.class);
|
||||||
|
final String fileName2 = "quick-2.txt";
|
||||||
|
final File file2 = getResourceFile(fileName2);
|
||||||
|
reqBody = MultiPartBuilder.create()
|
||||||
|
.setFileData(new FileData(fileName2, file2, MimetypeMap.MIMETYPE_TEXT_PLAIN))
|
||||||
|
.build();
|
||||||
|
post(getChildrenUrl(user1Home.getNodeRef()), user2, new String(reqBody.getBody()), null, reqBody.getContentType(), 403);
|
||||||
|
|
||||||
|
response = getAll(getChildrenUrl(userNodeAlias), user1, paging, 200);
|
||||||
|
pagingResult = parsePaging(response.getJsonResponse());
|
||||||
|
assertNotNull(paging);
|
||||||
|
assertEquals("Access Denied. The file shouldn't have been uploaded.", numOfNodes + 1, pagingResult.getCount().intValue());
|
||||||
|
|
||||||
|
// User1 tries to upload a file into a document rather than a folder!
|
||||||
|
post(getChildrenUrl(document.getNodeRef()), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 400);
|
||||||
|
|
||||||
|
// Try to upload a file without defining the required formData
|
||||||
|
reqBody = MultiPartBuilder.create().build();
|
||||||
|
post(getChildrenUrl(userNodeAlias), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests Multipart upload to a Site.
|
||||||
|
* <p>POST:</p>
|
||||||
|
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/children}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testUploadToSite() throws Exception
|
||||||
|
{
|
||||||
|
final String fileName = "quick-1.txt";
|
||||||
|
final File file = getResourceFile(fileName);
|
||||||
|
|
||||||
|
AuthenticationUtil.setFullyAuthenticatedUser(userOneN1.getId());
|
||||||
|
String folderA = "folder" + System.currentTimeMillis() + "_A";
|
||||||
|
NodeRef folderA_Ref = repoService.addToDocumentLibrary(userOneN1Site, folderA, ContentModel.TYPE_FOLDER);
|
||||||
|
|
||||||
|
Paging paging = getPaging(0, Integer.MAX_VALUE);
|
||||||
|
HttpResponse response = getAll(getChildrenUrl(folderA_Ref), userOneN1.getId(), paging, 200);
|
||||||
|
PublicApiClient.ExpectedPaging pagingResult = parsePaging(response.getJsonResponse());
|
||||||
|
assertNotNull(paging);
|
||||||
|
final int numOfNodes = pagingResult.getCount().intValue();
|
||||||
|
|
||||||
|
MultiPartBuilder multiPartBuilder = MultiPartBuilder.create()
|
||||||
|
.setFileData(new FileData(fileName, file, MimetypeMap.MIMETYPE_TEXT_PLAIN));
|
||||||
|
MultiPartRequest reqBody = multiPartBuilder.build();
|
||||||
|
// Try to upload
|
||||||
|
response = post(getChildrenUrl(folderA_Ref), userOneN1.getId(), new String(reqBody.getBody()), null, reqBody.getContentType(), 201);
|
||||||
|
Document document = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class);
|
||||||
|
// Check the upload response
|
||||||
|
assertEquals(fileName, document.getName());
|
||||||
|
ContentInfo contentInfo = document.getContent();
|
||||||
|
assertNotNull(contentInfo);
|
||||||
|
assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
|
||||||
|
|
||||||
|
// Retrieve the uploaded file
|
||||||
|
response = getSingle(NodesEntityResource.class, userOneN1.getId(), document.getNodeRef().getId(), null, 200);
|
||||||
|
document = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class);
|
||||||
|
assertEquals(fileName, document.getName());
|
||||||
|
contentInfo = document.getContent();
|
||||||
|
assertNotNull(contentInfo);
|
||||||
|
assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
|
||||||
|
|
||||||
|
// Check 'get children' is confirming the upload
|
||||||
|
response = getAll(getChildrenUrl(folderA_Ref), userOneN1.getId(), paging, 200);
|
||||||
|
pagingResult = parsePaging(response.getJsonResponse());
|
||||||
|
assertNotNull(paging);
|
||||||
|
assertEquals(numOfNodes + 1, pagingResult.getCount().intValue());
|
||||||
|
|
||||||
|
// Upload the same file again to check the name conflicts handling
|
||||||
|
post(getChildrenUrl(folderA_Ref), userOneN1.getId(), new String(reqBody.getBody()), null, reqBody.getContentType(), 409);
|
||||||
|
|
||||||
|
// Set overwrite=true and upload the same file again
|
||||||
|
reqBody = MultiPartBuilder.copy(multiPartBuilder)
|
||||||
|
.setOverwrite(true)
|
||||||
|
.build();
|
||||||
|
post(getChildrenUrl(folderA_Ref), userOneN1.getId(), new String(reqBody.getBody()), null, reqBody.getContentType(), 201);
|
||||||
|
|
||||||
|
response = getAll(getChildrenUrl(folderA_Ref), userOneN1.getId(), paging, 200);
|
||||||
|
pagingResult = parsePaging(response.getJsonResponse());
|
||||||
|
assertNotNull(paging);
|
||||||
|
assertEquals(numOfNodes + 1, pagingResult.getCount().intValue());
|
||||||
|
|
||||||
|
final String fileName2 = "quick-2.txt";
|
||||||
|
final File file2 = getResourceFile(fileName2);
|
||||||
|
reqBody = MultiPartBuilder.create()
|
||||||
|
.setFileData(new FileData(fileName2, file2, MimetypeMap.MIMETYPE_TEXT_PLAIN))
|
||||||
|
.build();
|
||||||
|
// userTwoN1 tries to upload a new file into the folderA of userOneN1
|
||||||
|
post(getChildrenUrl(folderA_Ref), userTwoN1.getId(), new String(reqBody.getBody()), null, reqBody.getContentType(), 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getChildrenUrl(NodeRef parentNodeRef)
|
||||||
|
{
|
||||||
|
return getChildrenUrl(parentNodeRef.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getChildrenUrl(String parentId)
|
||||||
|
{
|
||||||
|
return "nodes/" + parentId + "/children";
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getResourceFile(String fileName) throws FileNotFoundException
|
||||||
|
{
|
||||||
|
URL url = NodeApiTest.class.getClassLoader().getResource(RESOURCE_PREFIX + fileName);
|
||||||
|
if (url == null)
|
||||||
|
{
|
||||||
|
fail("Cannot get the resource: " + fileName);
|
||||||
|
}
|
||||||
|
return ResourceUtils.getFile(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -50,7 +50,7 @@ public class MultiPartBuilder
|
|||||||
private String contentTypeQNameStr;
|
private String contentTypeQNameStr;
|
||||||
private List<String> aspects;
|
private List<String> aspects;
|
||||||
private boolean majorVersion;
|
private boolean majorVersion;
|
||||||
private boolean overwrite = true; // If a fileName clashes for a versionable file
|
private boolean overwrite = false; // If a fileName clashes for a versionable file
|
||||||
|
|
||||||
private MultiPartBuilder()
|
private MultiPartBuilder()
|
||||||
{
|
{
|
||||||
@@ -225,11 +225,13 @@ public class MultiPartBuilder
|
|||||||
|
|
||||||
public MultiPartRequest build() throws IOException
|
public MultiPartRequest build() throws IOException
|
||||||
{
|
{
|
||||||
assertNotNull(fileData);
|
|
||||||
List<Part> parts = new ArrayList<>();
|
List<Part> parts = new ArrayList<>();
|
||||||
|
|
||||||
parts.add(new FilePart("filedata", fileData.getFileName(), fileData.getFile(), fileData.getMimetype(), null));
|
if (fileData != null)
|
||||||
addPartIfNotNull(parts, "filename", fileData.getFileName());
|
{
|
||||||
|
parts.add(new FilePart("filedata", fileData.getFileName(), fileData.getFile(), fileData.getMimetype(), null));
|
||||||
|
addPartIfNotNull(parts, "filename", fileData.getFileName());
|
||||||
|
}
|
||||||
addPartIfNotNull(parts, "siteid", siteId);
|
addPartIfNotNull(parts, "siteid", siteId);
|
||||||
addPartIfNotNull(parts, "containerid", containerId);
|
addPartIfNotNull(parts, "containerid", containerId);
|
||||||
addPartIfNotNull(parts, "destination", destination);
|
addPartIfNotNull(parts, "destination", destination);
|
||||||
|
1
source/test-resources/publicapi/upload/quick-1.txt
Normal file
1
source/test-resources/publicapi/upload/quick-1.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
The quick brown fox jumps over the lazy dog
|
2
source/test-resources/publicapi/upload/quick-2.txt
Normal file
2
source/test-resources/publicapi/upload/quick-2.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Gym class featuring a brown fox and lazy dog
|
||||||
|
The quick brown fox jumps over the lazy dog
|
BIN
source/test-resources/publicapi/upload/quick.pdf
Normal file
BIN
source/test-resources/publicapi/upload/quick.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user