From 543d2bbf1c74f83705305c3a685e8317c77fa025 Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Wed, 11 May 2016 12:04:10 +0000 Subject: [PATCH] Merged HEAD (5.2) to 5.2.N (5.2.1) 126538 jkaabimofrad: Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2) 123316 jvonka: Nodes (FileFolder) API - add optional include allowableOperations RA-770 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@126882 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- source/java/org/alfresco/rest/api/Nodes.java | 5 + .../alfresco/rest/api/QuickShareLinks.java | 2 +- .../org/alfresco/rest/api/impl/NodesImpl.java | 39 +++++-- .../rest/api/impl/QuickShareLinksImpl.java | 14 +-- .../org/alfresco/rest/api/model/Node.java | 35 +++++- .../alfresco/rest/api/tests/NodeApiTest.java | 102 ++++++++++++++++++ .../rest/api/tests/client/data/Node.java | 26 +++++ 7 files changed, 205 insertions(+), 18 deletions(-) diff --git a/source/java/org/alfresco/rest/api/Nodes.java b/source/java/org/alfresco/rest/api/Nodes.java index a5473c6646..59685c6b71 100644 --- a/source/java/org/alfresco/rest/api/Nodes.java +++ b/source/java/org/alfresco/rest/api/Nodes.java @@ -193,6 +193,10 @@ public interface Nodes String PATH_MY = "-my-"; String PATH_SHARED = "-shared-"; + String OP_CREATE = "create"; + String OP_DELETE= "delete"; + String OP_UPDATE = "update"; + String PARAM_RELATIVE_PATH = "relativePath"; String PARAM_AUTO_RENAME = "autoRename"; String PARAM_PERMANENT = "permanent"; @@ -201,6 +205,7 @@ public interface Nodes String PARAM_INCLUDE_PATH = "path"; String PARAM_INCLUDE_ASPECTNAMES = "aspectNames"; String PARAM_INCLUDE_ISLINK = "isLink"; + String PARAM_INCLUDE_ALLOWABLEOPERATIONS = "allowableOperations"; String PARAM_ISFOLDER = "isFolder"; String PARAM_ISFILE = "isFile"; diff --git a/source/java/org/alfresco/rest/api/QuickShareLinks.java b/source/java/org/alfresco/rest/api/QuickShareLinks.java index d2e45e23ef..18b46ce3b7 100644 --- a/source/java/org/alfresco/rest/api/QuickShareLinks.java +++ b/source/java/org/alfresco/rest/api/QuickShareLinks.java @@ -103,5 +103,5 @@ public interface QuickShareLinks */ String PARAM_SHAREDBY = "sharedByUser"; - String PARAM_SELECT_ISLINK = "allowableOperations"; + String PARAM_INCLUDE_ALLOWABLEOPERATIONS = Nodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS; } \ No newline at end of file diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java index ae23570a04..c3508e3db7 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -728,7 +728,7 @@ public class NodesImpl implements Nodes return getFolderOrDocument(nodeRef, parentNodeRef, nodeTypeQName, includeParam, null); } - private Node getFolderOrDocument(final NodeRef nodeRef, NodeRef parentNodeRef, QName nodeTypeQName, List selectParam, Map mapUserInfo) + private Node getFolderOrDocument(final NodeRef nodeRef, NodeRef parentNodeRef, QName nodeTypeQName, List includeParam, Map mapUserInfo) { if (mapUserInfo == null) { @@ -736,7 +736,7 @@ public class NodesImpl implements Nodes } PathInfo pathInfo = null; - if (selectParam.contains(PARAM_INCLUDE_PATH)) + if (includeParam.contains(PARAM_INCLUDE_PATH)) { pathInfo = lookupPathInfo(nodeRef); } @@ -777,21 +777,48 @@ public class NodesImpl implements Nodes throw new RuntimeException("Unexpected - should not reach here: "+type); } - if (selectParam.size() > 0) + if (includeParam.size() > 0) { - node.setProperties(mapFromNodeProperties(properties, selectParam, mapUserInfo)); + node.setProperties(mapFromNodeProperties(properties, includeParam, mapUserInfo)); } - if (selectParam.contains(PARAM_INCLUDE_ASPECTNAMES)) + if (includeParam.contains(PARAM_INCLUDE_ASPECTNAMES)) { node.setAspectNames(mapFromNodeAspects(nodeService.getAspects(nodeRef))); } - if (selectParam.contains(PARAM_INCLUDE_ISLINK)) + if (includeParam.contains(PARAM_INCLUDE_ISLINK)) { boolean isLink = isSubClass(nodeTypeQName, ContentModel.TYPE_LINK); node.setIsLink(isLink); } + + if (includeParam.contains(PARAM_INCLUDE_ALLOWABLEOPERATIONS)) + { + // note: refactor when requirements change + Map mapPermsToOps = new HashMap<>(3); + mapPermsToOps.put(PermissionService.DELETE, OP_DELETE); + mapPermsToOps.put(PermissionService.ADD_CHILDREN, OP_CREATE); + mapPermsToOps.put(PermissionService.WRITE, OP_UPDATE); + + List allowableOperations = new ArrayList<>(3); + for (Entry kv : mapPermsToOps.entrySet()) + { + String perm = kv.getKey(); + String op = kv.getValue(); + + // special case: do not return "create" for file + if (! (perm.equals(PermissionService.ADD_CHILDREN) && type.equals(Type.DOCUMENT))) + { + if (permissionService.hasPermission(nodeRef, perm) == AccessStatus.ALLOWED) + { + allowableOperations.add(op); + } + } + } + + node.setAllowableOperations((allowableOperations.size() > 0 )? allowableOperations : null); + } node.setNodeType(nodeTypeQName.toPrefixString(namespaceService)); node.setPath(pathInfo); diff --git a/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java b/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java index f4854b475a..95815b639a 100644 --- a/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java +++ b/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java @@ -455,23 +455,23 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean return CollectionWithPagingInfo.asPaged(paging, qsLinks, results.hasMore(), new Long(results.getNumberFound()).intValue()); } - private QuickShareLink getQuickShareInfo(String sharedId, boolean noAuth, List selectParam) + private QuickShareLink getQuickShareInfo(String sharedId, boolean noAuth, List includeParam) { checkValidShareId(sharedId); Map map = (Map) quickShareService.getMetaData(sharedId).get("item"); NodeRef nodeRef = new NodeRef((String) map.get("nodeRef")); - return getQuickShareInfo(nodeRef, map, noAuth, selectParam); + return getQuickShareInfo(nodeRef, map, noAuth, includeParam); } - private QuickShareLink getQuickShareInfo(NodeRef nodeRef, boolean noAuth, List selectParam) + private QuickShareLink getQuickShareInfo(NodeRef nodeRef, boolean noAuth, List includeParam) { Map map = (Map) quickShareService.getMetaData(nodeRef).get("item"); - return getQuickShareInfo(nodeRef, map , noAuth, selectParam); + return getQuickShareInfo(nodeRef, map , noAuth, includeParam); } - private QuickShareLink getQuickShareInfo(NodeRef nodeRef, Map map, boolean noAuth, List selectParam) + private QuickShareLink getQuickShareInfo(NodeRef nodeRef, Map map, boolean noAuth, List includeParam) { String sharedId = (String)map.get("sharedId"); @@ -502,11 +502,11 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean qs.setModifiedByUser(modifiedByUser); qs.setSharedByUser(sharedByUser); - if ((! noAuth) && selectParam.contains(PARAM_SELECT_ISLINK)) + if ((! noAuth) && includeParam.contains(PARAM_INCLUDE_ALLOWABLEOPERATIONS)) { if (canDeleteSharedLink(nodeRef, sharedByUserId)) { - qs.setAllowableOperations(Collections.singletonList("delete")); + qs.setAllowableOperations(Collections.singletonList(Nodes.OP_DELETE)); } } diff --git a/source/java/org/alfresco/rest/api/model/Node.java b/source/java/org/alfresco/rest/api/model/Node.java index 994af7ab4b..ad081366d4 100644 --- a/source/java/org/alfresco/rest/api/model/Node.java +++ b/source/java/org/alfresco/rest/api/model/Node.java @@ -74,6 +74,8 @@ public class Node implements Comparable protected List aspectNames; protected Map properties; + protected List allowableOperations; + public Node(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, Map mapUserInfo, ServiceRegistry sr) { if(nodeRef == null) @@ -285,6 +287,16 @@ public class Node implements Comparable this.isLink = isLink; } + public List getAllowableOperations() + { + return allowableOperations; + } + + public void setAllowableOperations(List allowableOperations) + { + this.allowableOperations = allowableOperations; + } + public boolean equals(Object other) { if(this == other) @@ -310,10 +322,25 @@ public class Node implements Comparable @Override public String toString() { - return "Node [nodeRef=" + nodeRef + ", type=" + prefixTypeQName + ", name=" + name + ", title=" - + title + ", description=" + description + ", createdAt=" - + createdAt + ", modifiedAt=" + modifiedAt + ", createdByUser=" + createdByUser + ", modifiedBy=" - + modifiedByUser + ", isFolder =" + isFolder + ", isFile =" + isFile + ", pathInfo =" + pathInfo +"]"; + StringBuilder sb = new StringBuilder(); + sb.append("Node [id=").append(getNodeRef().getId()); + sb.append(", parentId=").append(getParentId()); + sb.append(", type=").append(getNodeType()); + sb.append(", name=").append(getName()); + sb.append(", isFolder=").append(getIsFolder()); + sb.append(", isFile=").append(getIsFile()); + sb.append(", isLink=").append(getIsLink()); // note: symbolic link (not shared link) + sb.append(", modifiedAt=").append(getModifiedAt()); + sb.append(", modifiedByUser=").append(getModifiedByUser()); + sb.append(", createdAt=").append(getCreatedAt()); + sb.append(", createdByUser=").append(getCreatedByUser()); + sb.append(", path=").append(getPath()); + sb.append(", content=").append(getContent()); + sb.append(", aspectNames=").append(getAspectNames()); + //sb.append(", properties=").append(getProperties()); + sb.append(", allowableOperations=").append(getAllowableOperations()); + sb.append("]"); + return sb.toString(); } // here to allow POST /nodes/{id}/children when creating empty file with specified content.mimeType diff --git a/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java b/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java index b322818780..1a2f60a488 100644 --- a/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java @@ -130,6 +130,8 @@ public class NodeApiTest extends AbstractBaseApiTest protected NodeArchiveService nodeArchiveService; protected NodeService nodeService; + private final String RUNID = System.currentTimeMillis()+""; + @Before public void setup() throws Exception @@ -1134,6 +1136,7 @@ public class NodeApiTest extends AbstractBaseApiTest // admin can permanently delete String folder6Id = createFolder(user1, sharedNodeId, "folder " + runId + "_6").getId(); + params = Collections.singletonMap("permanent", "true"); // TODO improve - admin-related tests @@ -2611,6 +2614,105 @@ public class NodeApiTest extends AbstractBaseApiTest getSingle(getNodeContentUrl(contentNodeId), user1, null, null, headers, 304); } + /** + * Tests optional lookup of Allowable Operations (eg. when getting node info, listing node children, ...) + * + *

GET:

+ * {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes//children?include=allowableOperations} + * {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes/?include=allowableOperations} + */ + @Test + public void testAllowableOps() throws Exception + { + String sharedNodeId = getSharedNodeId(user1); + + // as user1 ... + + // create folder + Node nodeResp = createFolder(user1, sharedNodeId, "folder 1 - "+RUNID); + String folderId = nodeResp.getId(); + assertNull(nodeResp.getAllowableOperations()); + + HttpResponse response = getSingle(NodesEntityResource.class, user1, folderId, null, 200); + nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNull(nodeResp.getAllowableOperations()); + + Map params = new HashMap<>(); + params.put("include", "allowableOperations"); + response = getSingle(NodesEntityResource.class, user1, folderId, params, 200); + nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNotNull(nodeResp.getAllowableOperations()); + assertEquals(3, nodeResp.getAllowableOperations().size()); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_DELETE)); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_CREATE)); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_UPDATE)); + + // create file + nodeResp = createTextFile(user1, folderId, "my file - "+RUNID+".txt", "The quick brown fox jumps over the lazy dog"); + String fileId = nodeResp.getId(); + assertNull(nodeResp.getAllowableOperations()); + + response = getSingle(NodesEntityResource.class, user1, fileId, null, 200); + nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNull(nodeResp.getAllowableOperations()); + + params = new HashMap<>(); + params.put("include", "allowableOperations"); + response = getSingle(NodesEntityResource.class, user1, fileId, params, 200); + nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNotNull(nodeResp.getAllowableOperations()); + assertEquals(2, nodeResp.getAllowableOperations().size()); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_DELETE)); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_UPDATE)); + + // as user2 ... + + params = new HashMap<>(); + params.put("include", "allowableOperations"); + response = getSingle(NodesEntityResource.class, user2, folderId, params, 200); + nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNotNull(nodeResp.getAllowableOperations()); + assertEquals(1, nodeResp.getAllowableOperations().size()); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_CREATE)); + + params = new HashMap<>(); + params.put("include", "allowableOperations"); + response = getSingle(NodesEntityResource.class, user2, fileId, params, 200); + nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNull(nodeResp.getAllowableOperations()); + + // as admin ... + + // TODO improve - admin-related tests + params = new HashMap<>(); + params.put("include", "allowableOperations"); + publicApiClient.setRequestContext(new RequestContext("-default-", "admin", "admin")); + response = publicApiClient.get(NodesEntityResource.class, folderId, null, params); + nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNotNull(nodeResp.getAllowableOperations()); + assertEquals(3, nodeResp.getAllowableOperations().size()); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_DELETE)); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_CREATE)); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_UPDATE)); + + params = new HashMap<>(); + params.put("include", "allowableOperations"); + publicApiClient.setRequestContext(new RequestContext("-default-", "admin", "admin")); + response = publicApiClient.get(NodesEntityResource.class, fileId, null, params); + nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNotNull(nodeResp.getAllowableOperations()); + assertEquals(2, nodeResp.getAllowableOperations().size()); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_DELETE)); + assertTrue(nodeResp.getAllowableOperations().contains(Nodes.OP_UPDATE)); + + publicApiClient.setRequestContext(null); + + // as user1 ... + + // cleanup + delete(URL_NODES, user1, folderId, 204); + } + @Override public String getScope() { diff --git a/source/test-java/org/alfresco/rest/api/tests/client/data/Node.java b/source/test-java/org/alfresco/rest/api/tests/client/data/Node.java index 36e75f4d6e..1ebdc69bad 100644 --- a/source/test-java/org/alfresco/rest/api/tests/client/data/Node.java +++ b/source/test-java/org/alfresco/rest/api/tests/client/data/Node.java @@ -63,6 +63,8 @@ public class Node protected ContentInfo contentInfo; + protected List allowableOperations; + public Node() { } @@ -207,6 +209,16 @@ public class Node return this.contentInfo; } + public List getAllowableOperations() + { + return allowableOperations; + } + + public void setAllowableOperations(List allowableOperations) + { + this.allowableOperations = allowableOperations; + } + public void expected(Object o) { Node other = (Node) o; @@ -310,5 +322,19 @@ public class Node { assertNull(other.getContent()); } + + if (allowableOperations != null) + { + assertNotNull(other.getAllowableOperations()); + assertEquals("Expected: "+allowableOperations+", actual: "+other.getAllowableOperations(), allowableOperations.size(), other.getAllowableOperations().size()); + for (String allowableOperation : allowableOperations) + { + assertTrue(other.getAllowableOperations().contains(allowableOperation)); + } + } + else + { + assertNull(other.getAllowableOperations()); + } } } \ No newline at end of file