REPO-164 / REPO-1086 - V1 REST API: Lock Node

- review suggestions
   - added more test cases + utility methods

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@129751 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Ancuta Morarasu
2016-08-22 13:10:19 +00:00
parent c83c0521b2
commit ecec348d6c
4 changed files with 102 additions and 59 deletions

View File

@@ -2944,7 +2944,7 @@ public class NodesImpl implements Nodes
} }
lockInfo = validateLockInformation(lockInfo); lockInfo = validateLockInformation(lockInfo);
lockService.lock(nodeRef, lockInfo.getType(), lockInfo.getTimeToExpire(), lockInfo.getLifetime(), lockInfo.getIncludeChildren()); lockService.lock(nodeRef, lockInfo.getMappedType(), lockInfo.getTimeToExpire(), lockInfo.getLifetime(), lockInfo.getIncludeChildren());
return getFolderOrDocument(nodeId, parameters); return getFolderOrDocument(nodeId, parameters);
} }

View File

@@ -27,12 +27,14 @@ package org.alfresco.rest.api.model;
import org.alfresco.repo.lock.mem.Lifetime; import org.alfresco.repo.lock.mem.Lifetime;
import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.lock.LockType;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
/** /**
* Representation of a lock info * Representation of a lock info
* *
* @author Ancuta Morarasu * @author Ancuta Morarasu
*/ */
@JsonIgnoreProperties({"mappedType"})
public class LockInfo public class LockInfo
{ {
private Integer timeToExpire; private Integer timeToExpire;
@@ -85,11 +87,16 @@ public class LockInfo
return includeChildren; return includeChildren;
} }
public LockType getType() public LockType getMappedType()
{ {
return type != null ? type.getType() : null; return type != null ? type.getType() : null;
} }
public LockType2 getType()
{
return type;
}
public void setType(String type) public void setType(String type)
{ {
this.type = LockType2.valueOf(type); this.type = LockType2.valueOf(type);
@@ -110,9 +117,9 @@ public class LockInfo
{ {
final StringBuilder sb = new StringBuilder("LockInfo{"); final StringBuilder sb = new StringBuilder("LockInfo{");
sb.append("includeChildren='").append(includeChildren).append('\''); sb.append("includeChildren='").append(includeChildren).append('\'');
sb.append(", timeToExpire=").append(timeToExpire).append('\''); sb.append(", timeToExpire='").append(timeToExpire).append('\'');
sb.append(", type=").append(type).append('\''); sb.append(", type='").append(type).append('\'');
sb.append(", lifetime=").append(lifetime).append('\''); sb.append(", lifetime='").append(lifetime).append('\'');
sb.append('}'); sb.append('}');
return sb.toString(); return sb.toString();
} }

View File

@@ -45,7 +45,6 @@ import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.rest.api.tests.client.PublicApiHttpClient.BinaryPayload; import org.alfresco.rest.api.tests.client.PublicApiHttpClient.BinaryPayload;
import org.alfresco.rest.api.tests.client.PublicApiHttpClient.RequestBuilder; import org.alfresco.rest.api.tests.client.PublicApiHttpClient.RequestBuilder;
import org.alfresco.rest.api.tests.client.RequestContext; import org.alfresco.rest.api.tests.client.RequestContext;
import org.alfresco.rest.api.tests.client.data.Company;
import org.alfresco.rest.api.tests.client.data.ContentInfo; import org.alfresco.rest.api.tests.client.data.ContentInfo;
import org.alfresco.rest.api.tests.client.data.Document; import org.alfresco.rest.api.tests.client.data.Document;
import org.alfresco.rest.api.tests.client.data.Folder; import org.alfresco.rest.api.tests.client.data.Folder;
@@ -790,13 +789,13 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
protected Document lock(String nodeId, String body) throws Exception protected Document lock(String nodeId, String body) throws Exception
{ {
HttpResponse response = post("nodes/" + nodeId + "/lock", body, null, 200); HttpResponse response = post(getNodeOperationUrl(nodeId, "lock"), body, null, 200);
return RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); return RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
} }
protected Document unlock(String nodeId, String body) throws Exception protected Document unlock(String nodeId, String body) throws Exception
{ {
HttpResponse response = post("nodes/" + nodeId + "/unlock", body, null, 200); HttpResponse response = post(getNodeOperationUrl(nodeId, "unlock"), body, null, 200);
return RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); return RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
} }
@@ -872,5 +871,10 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
{ {
return URL_NODES + "/" + nodeId + "/" + URL_CONTENT; return URL_NODES + "/" + nodeId + "/" + URL_CONTENT;
} }
protected String getNodeOperationUrl(String nodeId, String operation)
{
return URL_NODES + "/" + nodeId + "/" + operation;
}
} }

View File

@@ -57,8 +57,10 @@ import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.rest.AbstractSingleNetworkSiteTest; import org.alfresco.rest.AbstractSingleNetworkSiteTest;
import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.LockInfo;
import org.alfresco.rest.api.model.NodeTarget; import org.alfresco.rest.api.model.NodeTarget;
import org.alfresco.rest.api.model.Site; import org.alfresco.rest.api.model.Site;
import org.alfresco.rest.api.model.UnlockInfo;
import org.alfresco.rest.api.nodes.NodesEntityResource; import org.alfresco.rest.api.nodes.NodesEntityResource;
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;
@@ -3608,13 +3610,13 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertNull(node.getProperties().get("cm:lockOwner")); assertNull(node.getProperties().get("cm:lockOwner"));
assertFalse(node.getIsLocked()); assertFalse(node.getIsLocked());
Map<String, String> body = new HashMap<>(); LockInfo lockInfo = new LockInfo();
body.put("includeChildren", "true"); lockInfo.setIncludeChildren(true);
body.put("timeToExpire", "60"); lockInfo.setTimeToExpire(60);
body.put("type", "FULL"); lockInfo.setType("FULL");
body.put("lifetime", "PERSISTENT"); lockInfo.setLifetime("PERSISTENT");
response = post(URL_NODES, d1Id, "lock", toJsonAsStringNonNull(body).getBytes(), null, null, 200); response = post(getNodeOperationUrl(d1Id, "lock"), toJsonAsStringNonNull(lockInfo), null, 200);
Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
assertEquals(d1Name, documentResp.getName()); assertEquals(d1Name, documentResp.getName());
@@ -3624,7 +3626,7 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertNull(documentResp.getIsLocked()); assertNull(documentResp.getIsLocked());
// Empty lock body, the default values are used // Empty lock body, the default values are used
post("nodes/"+folderId+"/lock", EMPTY_BODY, null, 200); post(getNodeOperationUrl(folderId, "lock"), EMPTY_BODY, null, 200);
// Test delete on a folder which contains a locked node - NodeLockedException // Test delete on a folder which contains a locked node - NodeLockedException
deleteNode(folderId, true, HttpStatus.SC_CONFLICT); deleteNode(folderId, true, HttpStatus.SC_CONFLICT);
@@ -3648,7 +3650,7 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
params = Collections.singletonMap("include", "aspectNames,properties,isLocked"); params = Collections.singletonMap("include", "aspectNames,properties,isLocked");
response = getAll(getNodeChildrenUrl(folderAId), null, params, 200); response = getAll(getNodeChildrenUrl(folderAId), null, params, 200);
List<Node> nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); List<Node> nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
// Check children nodes are not locked. // check children nodes are not locked.
for (Node child : nodes) for (Node child : nodes)
{ {
assertNull(child.getProperties().get("cm:lockType")); assertNull(child.getProperties().get("cm:lockType"));
@@ -3656,14 +3658,14 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertFalse(child.getIsLocked()); assertFalse(child.getIsLocked());
} }
body = new HashMap<>(); lockInfo = new LockInfo();
body.put("includeChildren", "true"); lockInfo.setIncludeChildren(true);
body.put("timeToExpire", "60"); lockInfo.setTimeToExpire(60);
body.put("type", "FULL"); lockInfo.setType("ALLOW_OWNER_CHANGES");
body.put("lifetime", "EPHEMERAL"); lockInfo.setLifetime("EPHEMERAL");
// lock the folder // lock the folder
response = post(URL_NODES, folderAId, "lock", toJsonAsStringNonNull(body).getBytes(), null, null, 200); response = post(getNodeOperationUrl(folderAId, "lock"), toJsonAsStringNonNull(lockInfo), null, 200);
documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
assertEquals(folderAName, documentResp.getName()); assertEquals(folderAName, documentResp.getName());
@@ -3675,7 +3677,7 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
params = Collections.singletonMap("include", "aspectNames,properties,isLocked"); params = Collections.singletonMap("include", "aspectNames,properties,isLocked");
response = getAll(getNodeChildrenUrl(folderAId), null, params, 200); response = getAll(getNodeChildrenUrl(folderAId), null, params, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
// Test if children nodes are locked as well. // test if children nodes are locked as well.
for (Node child : nodes) for (Node child : nodes)
{ {
assertNotNull(child.getProperties().get("cm:lockType")); assertNotNull(child.getProperties().get("cm:lockType"));
@@ -3683,20 +3685,31 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertTrue(child.getIsLocked()); assertTrue(child.getIsLocked());
} }
// Lock body properties - boundary values
Folder folderB = createFolder(Nodes.PATH_MY, "folder" + RUNID + "_B"); Folder folderB = createFolder(Nodes.PATH_MY, "folder" + RUNID + "_B");
String folderBId = folderB.getId(); String folderBId = folderB.getId();
body = new HashMap<>(); lockInfo = new LockInfo();
body.put("timeToExpire", "-100"); // values lower than 0 are considered as no expiry time lockInfo.setTimeToExpire(-100); // values lower than 0 are considered as no expiry time
post("nodes/" + folderBId + "/lock", toJsonAsStringNonNull(body), null, 200); post(getNodeOperationUrl(folderBId, "lock"), toJsonAsStringNonNull(lockInfo), null, 200);
// Lock node by a different user than the owner
setRequestContext(user1);
Folder folder1Resp = createFolder(Nodes.PATH_SHARED, "folder1" + RUNID);
String folder1Id = folder1Resp.getId();
String f1d1Name = "content f1" + RUNID + "_1l";
Document f1d1 = createTextFile(folder1Id, f1d1Name, "The quick brown fox jumps over the lazy dog 1.");
String f1d1Id = f1d1.getId();
// use admin for now (might be better to use a user with given WRITE permission)
setRequestContext(networkAdmin);
post(getNodeOperationUrl(f1d1Id, "lock"), EMPTY_BODY, null, 200);
unlock(f1d1Id, EMPTY_BODY);
// -ve tests // -ve tests
// Missing target node // Missing target node
body = new HashMap<>(); lockInfo = new LockInfo();
body.put("timeToExpire", "60"); post(getNodeOperationUrl("fakeId", "lock"), toJsonAsStringNonNull(lockInfo), null, 404);
post("nodes/" + "fakeId" + "/lock", toJsonAsStringNonNull(body), null, 404);
// Cannot lock Data Dictionary node // Cannot lock Data Dictionary node
params = new HashMap<>(); params = new HashMap<>();
@@ -3706,45 +3719,61 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
String ddNodeId = nodeResp.getId(); String ddNodeId = nodeResp.getId();
setRequestContext(networkAdmin); setRequestContext(networkAdmin);
post("nodes/" + ddNodeId + "/lock", toJsonAsStringNonNull(body), null, 403); post(getNodeOperationUrl(ddNodeId, "lock"), toJsonAsStringNonNull(lockInfo), null, 403);
// Lock node already locked by another user - UnableToAquireLockException // Lock node already locked by another user - UnableToAquireLockException
post("nodes/" + folderId + "/lock", EMPTY_BODY, null, 422); post(getNodeOperationUrl(folderId, "lock"), EMPTY_BODY, null, 422);
// Lock node without permission (node created by user 1 in the Home folder)
setRequestContext(user1);
Folder folder2Resp = createFolder(Nodes.PATH_MY, "folder2" + RUNID);
String folder2Id = folder2Resp.getId();
String f2d1Name = "content f2" + RUNID + "_1l";
Document f2d1 = createTextFile(folder2Id, f2d1Name, "The quick brown fox jumps over the lazy dog 1.");
String f2d1Id = f2d1.getId();
setRequestContext(user2);
post(getNodeOperationUrl(f2d1Id, "lock"), EMPTY_BODY, null, 403);
// Invalid lock body values // Invalid lock body values
setRequestContext(user1); setRequestContext(user1);
Folder folderC = createFolder(Nodes.PATH_MY, "folder" + RUNID + "_C"); Folder folderC = createFolder(Nodes.PATH_MY, "folder" + RUNID + "_C");
String folderCId = folderC.getId(); String folderCId = folderC.getId();
body = new HashMap<>(); Map<String, String> body = new HashMap<>();
body.put("includeChildren", "true123"); body.put("includeChildren", "true123");
post("nodes/" + folderCId + "/lock", toJsonAsStringNonNull(body), null, 400); post(getNodeOperationUrl(folderCId, "lock"), toJsonAsStringNonNull(body), null, 400);
body = new HashMap<>(); body = new HashMap<>();
body.put("type", "FULL123"); body.put("type", "FULL123");
post("nodes/" + folderCId + "/lock", toJsonAsStringNonNull(body), null, 400); post(getNodeOperationUrl(folderCId, "lock"), toJsonAsStringNonNull(body), null, 400);
body = new HashMap<>(); body = new HashMap<>();
body.put("lifetime", "PERSISTENT123"); body.put("lifetime", "PERSISTENT123");
post("nodes/" + folderCId + "/lock", toJsonAsStringNonNull(body), null, 400); post(getNodeOperationUrl(folderCId, "lock"), toJsonAsStringNonNull(body), null, 400);
body = new HashMap<>(); body = new HashMap<>();
body.put("timeToExpire", "NaN"); body.put("timeToExpire", "NaN");
post("nodes/" + folderCId + "/lock", toJsonAsStringNonNull(body), null, 400); post(getNodeOperationUrl(folderCId, "lock"), toJsonAsStringNonNull(body), null, 400);
body = new HashMap<>(); body = new HashMap<>();
body.put("invalid_property", "true"); body.put("invalid_property", "true");
post("nodes/" + folderCId + "/lock", toJsonAsStringNonNull(body), null, 400); post(getNodeOperationUrl(folderCId, "lock"), toJsonAsStringNonNull(body), null, 400);
//cleanup //cleanup
setRequestContext(user1); // all locks were made by user1 setRequestContext(user1); // all locks were made by user1
unlock(folderId, toJsonAsStringNonNull(Collections.singletonMap("includeChildren", "true"))); UnlockInfo unlockInfo = new UnlockInfo();
unlockInfo.setIncludeChildren(true);
unlock(folderId, toJsonAsStringNonNull(unlockInfo));
deleteNode(folderId); deleteNode(folderId);
unlock(folderAId, toJsonAsStringNonNull(Collections.singletonMap("includeChildren", "true"))); unlock(folderAId, toJsonAsStringNonNull(unlockInfo));
deleteNode(folderAId); deleteNode(folderAId);
unlock(folderBId, EMPTY_BODY); unlock(folderBId, EMPTY_BODY);
deleteNode(folderBId); deleteNode(folderBId);
deleteNode(folderCId); deleteNode(folderCId);
deleteNode(folder1Id);
deleteNode(folder2Id);
} }
@@ -3769,11 +3798,11 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
lock(d1Id, EMPTY_BODY); lock(d1Id, EMPTY_BODY);
Map<String, String> body = new HashMap<>(); UnlockInfo unlockInfo = new UnlockInfo();
body.put("includeChildren", "true"); unlockInfo.setIncludeChildren(true);
body.put("allowCheckedOut", "true"); unlockInfo.setAllowCheckedOut(true);
HttpResponse response = post(URL_NODES, d1Id, "unlock", toJsonAsStringNonNull(body).getBytes(), null, null, 200); HttpResponse response = post(getNodeOperationUrl(d1Id, "unlock"), toJsonAsStringNonNull(unlockInfo), null, 200);
Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
assertEquals(d1Name, documentResp.getName()); assertEquals(d1Name, documentResp.getName());
@@ -3784,13 +3813,13 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
lock(d1Id, EMPTY_BODY); lock(d1Id, EMPTY_BODY);
// Users with admin rights can unlock nodes locked by other users. // Users with admin rights can unlock nodes locked by other users.
setRequestContext(networkAdmin); setRequestContext(networkAdmin);
post("nodes/" + d1Id + "/unlock", EMPTY_BODY, null, 200); post(getNodeOperationUrl(d1Id, "unlock"), EMPTY_BODY, null, 200);
setRequestContext(user1); setRequestContext(user1);
//Unlock on a not locked node should do nothing //Unlock on a not locked node should do nothing
post("nodes/" + d1Id + "/unlock", EMPTY_BODY, null, 200); post(getNodeOperationUrl(d1Id, "unlock"), EMPTY_BODY, null, 200);
post("nodes/" + folderId + "/unlock", EMPTY_BODY, null, 200); post(getNodeOperationUrl(folderId, "unlock"), EMPTY_BODY, null, 200);
// Test unlock children // Test unlock children
// create folder // create folder
@@ -3807,13 +3836,14 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
String dA2Id = dA2.getId(); String dA2Id = dA2.getId();
// lock the folder and children // lock the folder and children
body = new HashMap<>(); Map<String, String> body = new HashMap<>();
body.put("includeChildren", "true"); body.put("includeChildren", "true");
lock(folderAId, toJsonAsStringNonNull(body)); lock(folderAId, toJsonAsStringNonNull(body));
body.put("includeChildren", "true"); unlockInfo = new UnlockInfo();
body.put("allowCheckedOut", "true"); unlockInfo.setIncludeChildren(true);
post(URL_NODES, folderAId, "unlock", toJsonAsStringNonNull(body).getBytes(), null, null, 200); unlockInfo.setAllowCheckedOut(true);
post(getNodeOperationUrl(folderAId, "unlock"), toJsonAsStringNonNull(unlockInfo), null, 200);
Map<String, String> params = Collections.singletonMap("include", "aspectNames,properties,isLocked"); Map<String, String> params = Collections.singletonMap("include", "aspectNames,properties,isLocked");
response = getAll(getNodeChildrenUrl(folderAId), null, params, 200); response = getAll(getNodeChildrenUrl(folderAId), null, params, 200);
@@ -3828,12 +3858,12 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
// -ve // -ve
// Missing target node // Missing target node
post("nodes/" + "fakeId" + "/unlock", EMPTY_BODY, null, 404); post(getNodeOperationUrl("fakeId", "unlock"), EMPTY_BODY, null, 404);
// Unlock by a user without permission // Unlock by a user without permission
lock(d1Id, EMPTY_BODY); lock(d1Id, EMPTY_BODY);
setRequestContext(user2); setRequestContext(user2);
post("nodes/" + d1Id + "/unlock", EMPTY_BODY, null, 403); post(getNodeOperationUrl(d1Id, "unlock"), EMPTY_BODY, null, 403);
// Invalid lock body values // Invalid lock body values
setRequestContext(user1); setRequestContext(user1);
@@ -3842,21 +3872,23 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
lock(folderCId, EMPTY_BODY); lock(folderCId, EMPTY_BODY);
body = new HashMap<>(); body = new HashMap<>();
body.put("includeChildren", "true123"); body.put("includeChildren", "true123");
post("nodes/" + folderCId + "/unlock", toJsonAsStringNonNull(body), null, 400); post(getNodeOperationUrl(folderCId, "unlock"), toJsonAsStringNonNull(body), null, 400);
body = new HashMap<>(); body = new HashMap<>();
body.put("allowCheckedOut", "false123"); body.put("allowCheckedOut", "false123");
post("nodes/" + folderCId + "/unlock", toJsonAsStringNonNull(body), null, 400); post(getNodeOperationUrl(folderCId, "unlock"), toJsonAsStringNonNull(body), null, 400);
body = new HashMap<>(); body = new HashMap<>();
body.put("invalid_property", "true"); body.put("invalid_property", "true");
post("nodes/" + folderCId + "/unlock", toJsonAsStringNonNull(body), null, 400); post(getNodeOperationUrl(folderCId, "unlock"), toJsonAsStringNonNull(body), null, 400);
// clean up // clean up
setRequestContext(user1); // all locks were made by user1 setRequestContext(user1); // all locks were made by user1
unlock(folderId, toJsonAsStringNonNull(Collections.singletonMap("includeChildren", "true"))); unlockInfo = new UnlockInfo();
unlockInfo.setIncludeChildren(true);
unlock(folderId, toJsonAsStringNonNull(unlockInfo));
deleteNode(folderId); deleteNode(folderId);
unlock(folderAId, toJsonAsStringNonNull(Collections.singletonMap("includeChildren", "true"))); unlock(folderAId, toJsonAsStringNonNull(unlockInfo));
deleteNode(folderAId); deleteNode(folderAId);
unlock(folderCId, EMPTY_BODY); unlock(folderCId, EMPTY_BODY);
deleteNode(folderCId); deleteNode(folderCId);