Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2)

120807 jvonka: FileFolder API - create/update node will return 400 for unknown property or aspect (not in dictionary models)
   - we no longer ignore invalid property and now also return correct error code for invalid aspect
   - update NodeApiTest and A/C of JIRAs ( RA-635, RA-636, RA-637, RA-638 )
   - note: in the future, we could consider option (eg. via query param) to allow residual props on create or update (or at least ability to nullify)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@126398 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2016-05-10 10:43:44 +00:00
parent f03a6e9e0e
commit 9c2e9983ff
4 changed files with 68 additions and 39 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2005-2015 Alfresco Software Limited. * Copyright (C) 2005-2016 Alfresco Software Limited.
* *
* This file is part of Alfresco * This file is part of Alfresco
* *
@@ -90,7 +90,7 @@ public interface Nodes
* - incFiles, incFolders (both true by default) * - incFiles, incFolders (both true by default)
* @return a paged list of {@code org.alfresco.rest.api.model.Node} objects * @return a paged list of {@code org.alfresco.rest.api.model.Node} objects
*/ */
CollectionWithPagingInfo<Node> getChildren(String parentFolderNodeId, Parameters parameters); CollectionWithPagingInfo<Node> listChildren(String parentFolderNodeId, Parameters parameters);
/** /**
* Delete the given node. Note: will cascade delete for a folder. * Delete the given node. Note: will cascade delete for a folder.

View File

@@ -18,6 +18,20 @@
*/ */
package org.alfresco.rest.api.impl; package org.alfresco.rest.api.impl;
import java.io.InputStream;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
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;
import org.alfresco.model.ApplicationModel; import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingRequest;
@@ -55,6 +69,7 @@ import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -86,20 +101,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.Content; import org.springframework.extensions.surf.util.Content;
import org.springframework.extensions.webscripts.servlet.FormData; import org.springframework.extensions.webscripts.servlet.FormData;
import java.io.InputStream;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
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;
/** /**
* Centralises access to file/folder/node services and maps between representations. * Centralises access to file/folder/node services and maps between representations.
* *
@@ -645,13 +646,13 @@ public class NodesImpl implements Nodes
boolean isLink = typeMatches(nodeTypeQName, Collections.singleton(ContentModel.TYPE_LINK), null); boolean isLink = typeMatches(nodeTypeQName, Collections.singleton(ContentModel.TYPE_LINK), null);
node.setIsLink(isLink); node.setIsLink(isLink);
} }
node.setNodeType(nodeTypeQName.toPrefixString(namespaceService)); node.setNodeType(nodeTypeQName.toPrefixString(namespaceService));
node.setPath(pathInfo); node.setPath(pathInfo);
return node; return node;
} }
protected PathInfo lookupPathInfo(NodeRef nodeRefIn) protected PathInfo lookupPathInfo(NodeRef nodeRefIn)
{ {
final Path nodePath = nodeService.getPath(nodeRefIn); final Path nodePath = nodeService.getPath(nodeRefIn);
@@ -701,6 +702,28 @@ public class NodesImpl implements Nodes
} }
return new PathInfo(pathStr, isComplete, pathElements); return new PathInfo(pathStr, isComplete, pathElements);
} }
protected Set<QName> mapToNodeAspects(List<String> aspectNames)
{
Set<QName> nodeAspects = new HashSet<>(aspectNames.size());
for (String aspectName : aspectNames)
{
QName aspectQName = createQName(aspectName);
AspectDefinition ad = dictionaryService.getAspect(aspectQName);
if (ad != null)
{
nodeAspects.add(aspectQName);
}
else
{
throw new InvalidArgumentException("Unknown aspect: "+aspectName);
}
}
return nodeAspects;
}
protected Map<QName, Serializable> mapToNodeProperties(Map<String, Object> props) protected Map<QName, Serializable> mapToNodeProperties(Map<String, Object> props)
{ {
@@ -708,7 +731,8 @@ public class NodesImpl implements Nodes
for (Entry<String, Object> entry : props.entrySet()) for (Entry<String, Object> entry : props.entrySet())
{ {
QName propQName = createQName(entry.getKey()); String propName = entry.getKey();
QName propQName = createQName(propName);
PropertyDefinition pd = dictionaryService.getProperty(propQName); PropertyDefinition pd = dictionaryService.getProperty(propQName);
if (pd != null) if (pd != null)
@@ -732,6 +756,10 @@ public class NodesImpl implements Nodes
} }
nodeProps.put(propQName, value); nodeProps.put(propQName, value);
} }
else
{
throw new InvalidArgumentException("Unknown property: "+propName);
}
} }
return nodeProps; return nodeProps;
@@ -805,7 +833,7 @@ public class NodesImpl implements Nodes
return aspectNames; return aspectNames;
} }
public CollectionWithPagingInfo<Node> getChildren(String parentFolderNodeId, Parameters parameters) public CollectionWithPagingInfo<Node> listChildren(String parentFolderNodeId, Parameters parameters)
{ {
final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null); final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null);
@@ -818,7 +846,7 @@ public class NodesImpl implements Nodes
if (q != null) if (q != null)
{ {
// TODO confirm list of filter props - what about custom props (+ across types/aspects) ? // TODO confirm list of filter props - what about custom props (+ across types/aspects) ? What about VF extension ?
MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(LIST_FOLDER_CHILDREN_EQUALS_QUERY_PROPERTIES, null); MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(LIST_FOLDER_CHILDREN_EQUALS_QUERY_PROPERTIES, null);
QueryHelper.walk(q, propertyWalker); QueryHelper.walk(q, propertyWalker);
@@ -950,9 +978,9 @@ public class NodesImpl implements Nodes
if (aspectNames != null) if (aspectNames != null)
{ {
// node aspects - set any additional aspects // node aspects - set any additional aspects
for (String aspectName : aspectNames) Set<QName> aspectQNames = mapToNodeAspects(aspectNames);
for (QName aspectQName : aspectQNames)
{ {
QName aspectQName = createQName(aspectName);
if (EXCLUDED_ASPECTS.contains(aspectQName) || aspectQName.equals(ContentModel.ASPECT_AUDITABLE)) if (EXCLUDED_ASPECTS.contains(aspectQName) || aspectQName.equals(ContentModel.ASPECT_AUDITABLE))
{ {
continue; // ignore continue; // ignore
@@ -1048,12 +1076,8 @@ public class NodesImpl implements Nodes
if (aspectNames != null) if (aspectNames != null)
{ {
// update aspects - note: can be empty (eg. to remove existing aspects+properties) but not cm:auditable, sys:referencable, sys:localized // update aspects - note: can be empty (eg. to remove existing aspects+properties) but not cm:auditable, sys:referencable, sys:localized
Set<QName> aspectQNames = new HashSet<>(aspectNames.size());
for (String aspectName : aspectNames) Set<QName> aspectQNames = mapToNodeAspects(aspectNames);
{
QName aspectQName = createQName(aspectName);
aspectQNames.add(aspectQName);
}
Set<QName> existingAspects = nodeService.getAspects(nodeRef); Set<QName> existingAspects = nodeService.getAspects(nodeRef);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2005-2015 Alfresco Software Limited. * Copyright (C) 2005-2016 Alfresco Software Limited.
* *
* This file is part of Alfresco * This file is part of Alfresco
* *
@@ -18,6 +18,9 @@
*/ */
package org.alfresco.rest.api.nodes; package org.alfresco.rest.api.nodes;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.Nodes;
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;
@@ -31,9 +34,6 @@ 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 org.springframework.extensions.webscripts.servlet.FormData;
import java.util.ArrayList;
import java.util.List;
/** /**
* Node Children * Node Children
* *
@@ -83,7 +83,7 @@ public class NodeChildrenRelation implements RelationshipResourceAction.Read<Nod
@WebApiDescription(title = "Return a paged list of nodes for the document/folder identified by parentFolderNodeId") @WebApiDescription(title = "Return a paged list of nodes for the document/folder identified by parentFolderNodeId")
public CollectionWithPagingInfo<Node> readAll(String parentFolderNodeId, Parameters parameters) public CollectionWithPagingInfo<Node> readAll(String parentFolderNodeId, Parameters parameters)
{ {
return nodes.getChildren(parentFolderNodeId, parameters); return nodes.listChildren(parentFolderNodeId, parameters);
} }
/** /**

View File

@@ -196,7 +196,7 @@ public class NodeApiTest extends AbstractBaseApiTest
repoService.addToDocumentLibrary(userOneN1Site, folder2, ContentModel.TYPE_FOLDER); repoService.addToDocumentLibrary(userOneN1Site, folder2, ContentModel.TYPE_FOLDER);
String content1 = "content" + System.currentTimeMillis() + "_1"; String content1 = "content" + System.currentTimeMillis() + "_1";
NodeRef contentNodeRef = repoService.addToDocumentLibrary(userOneN1Site, content1, ContentModel.TYPE_CONTENT); repoService.addToDocumentLibrary(userOneN1Site, content1, ContentModel.TYPE_CONTENT);
String content2 = "content" + System.currentTimeMillis() + "_2"; String content2 = "content" + System.currentTimeMillis() + "_2";
repoService.addToDocumentLibrary(userOneN1Site, content2, ContentModel.TYPE_CONTENT); repoService.addToDocumentLibrary(userOneN1Site, content2, ContentModel.TYPE_CONTENT);
@@ -1256,14 +1256,19 @@ public class NodeApiTest extends AbstractBaseApiTest
f1.setNodeType("app:glossary"); f1.setNodeType("app:glossary");
f1.expected(folderResp); f1.expected(folderResp);
// -ve test - ignore unknown property // -ve test - fail on unknown property
props = new HashMap<>(); props = new HashMap<>();
props.put("cm:xyz","my unknown property"); props.put("cm:xyz","my unknown property");
dUpdate = new Document(); dUpdate = new Document();
dUpdate.setProperties(props); dUpdate.setProperties(props);
response = put("nodes", user1, dId, toJsonAsStringNonNull(dUpdate), null, 200); put("nodes", user1, dId, toJsonAsStringNonNull(dUpdate), null, 400);
documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
d1.expected(documentResp); // -ve test - fail on unknown aspect
List<String> aspects = new ArrayList<>(d1.getAspectNames());
aspects.add("cm:unknownAspect");
dUpdate = new Document();
dUpdate.setAspectNames(aspects);
put("nodes", user1, dId, toJsonAsStringNonNull(dUpdate), null, 400);
// -ve test - duplicate name // -ve test - duplicate name
dUpdate = new Document(); dUpdate = new Document();