expectedIds = new ArrayList<>(5);
expectedIds.addAll(Arrays.asList(parentIds));
skipCount=0;
maxItems=2;
paging = getPaging(skipCount, maxItems);
response = getAll(getNodeParentsUrl(childId), paging, params, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(maxItems, nodes.size());
for (Node node : nodes)
{
expectedIds.remove(node.getId());
}
assertEquals(3, expectedIds.size());
expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse());
assertEquals(maxItems, expectedPaging.getCount().intValue());
assertEquals(skipCount, expectedPaging.getSkipCount().intValue());
assertEquals(maxItems, expectedPaging.getMaxItems().intValue());
assertTrue(expectedPaging.getHasMoreItems().booleanValue());
skipCount=2;
maxItems=2;
paging = getPaging(skipCount, maxItems);
response = getAll(getNodeParentsUrl(childId), paging, params, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(maxItems, nodes.size());
for (Node node : nodes)
{
expectedIds.remove(node.getId());
}
assertEquals(1, expectedIds.size());
expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse());
assertEquals(maxItems, expectedPaging.getCount().intValue());
assertEquals(skipCount, expectedPaging.getSkipCount().intValue());
assertEquals(maxItems, expectedPaging.getMaxItems().intValue());
assertTrue(expectedPaging.getHasMoreItems().booleanValue());
skipCount=4;
maxItems=2;
paging = getPaging(skipCount, maxItems);
response = getAll(getNodeParentsUrl(childId), paging, params, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
for (Node node : nodes)
{
expectedIds.remove(node.getId());
}
assertEquals(0, expectedIds.size());
expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse());
assertEquals(1, expectedPaging.getCount().intValue());
assertEquals(skipCount, expectedPaging.getSkipCount().intValue());
assertEquals(maxItems, expectedPaging.getMaxItems().intValue());
assertFalse(expectedPaging.getHasMoreItems().booleanValue());
}
//
// -ve tests - add assoc
//
{
setRequestContext(null);
// -ve test - unauthenticated - belts-and-braces ;-)
secChild = new AssocChild(o2Id, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(f1Id), toJsonAsStringNonNull(secChild), 401);
setRequestContext(user1);
// -ve test - model integrity
secChild = new AssocChild(o2Id, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(o1Id), toJsonAsStringNonNull(secChild), 422);
// -ve test - duplicate assoc
secChild = new AssocChild(o2Id, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(f1Id), toJsonAsStringNonNull(secChild), 201);
post(getNodeSecondaryChildrenUrl(f1Id), toJsonAsStringNonNull(secChild), 409);
delete(getNodeSecondaryChildrenUrl(f1Id), o2Id, null, 204); // cleanup
secChild = new AssocChild(o2Id, "cm:unknowntype");
post(getNodeSecondaryChildrenUrl(f1Id), toJsonAsStringNonNull(secChild), 400);
}
//
// -ve test - list assocs
//
{
setRequestContext(null);
// -ve test - unauthenticated - belts-and-braces ;-)
getAll(getNodeSecondaryChildrenUrl(f1Id), paging, null, 401);
getAll(getNodeParentsUrl(o2Id), paging, null, 401);
setRequestContext(user1);
getAll(getNodeSecondaryChildrenUrl(UUID.randomUUID().toString()), paging, null, 404);
getAll(getNodeParentsUrl(UUID.randomUUID().toString()), paging, null, 404);
params = new HashMap<>(1);
params.put("where", "(assocType='cm:unknownassoctype')");
getAll(getNodeSecondaryChildrenUrl(o1Id), paging, params, 400);
getAll(getNodeParentsUrl(o1Id), paging, params, 400);
}
//
// -ve test - remove assoc(s)
//
{
setRequestContext(null);
// unauthenticated - belts-and-braces ;-)
delete(getNodeSecondaryChildrenUrl(f1Id), o2Id, null, 401);
setRequestContext(user1);
delete(getNodeSecondaryChildrenUrl(UUID.randomUUID().toString()), o2Id, null, 404);
delete(getNodeSecondaryChildrenUrl(f1Id), UUID.randomUUID().toString(), null, 404);
// nothing to remove - for any assoc type
delete(getNodeSecondaryChildrenUrl(f1Id), o2Id, null, 404);
// nothing to remove - for given assoc type
params = new HashMap<>(1);
params.put(PARAM_ASSOC_TYPE, ASSOC_TYPE_CM_PREFERENCE_IMAGE);
delete(getNodeSecondaryChildrenUrl(f1Id), o2Id, params, 404);
// unknown assoc type
params = new HashMap<>(1);
params.put(PARAM_ASSOC_TYPE, "cm:unknowntype");
delete(getNodeSecondaryChildrenUrl(o1Id), o2Id, params, 400);
// do not allow delete of primary child (via secondary child removal)
params = new HashMap<>(1);
params.put(PARAM_ASSOC_TYPE, "cm:contains");
delete(getNodeSecondaryChildrenUrl(f1Id), o1Id, params, 400);
}
}
finally
{
// some cleanup
setRequestContext(user1);
deleteNode(f1Id, true, 204);
deleteNode(f2Id, true, 204);
deleteNode(f3Id, true, 204);
deleteNode(f4Id, true, 204);
}
}
/**
* Regardless of which parent is used to list children of a node, the node information
* should consistently present the primary parent as a node's {@code parentId}.
*
* See REPO-1780
*/
@Test
public void testListChildrenConsistentParentIdWithSecondaryAssociations() throws Exception
{
setRequestContext(user1);
String myNodeId = getMyNodeId();
// Create primary folder
String primaryFolderName = "primary folder " + RUNID;
String primaryFolderId = createFolder(myNodeId, primaryFolderName, null).getId();
// Create content file
String contentFileName = "content" + RUNID + " in folder";
String contentId = createTextFile(primaryFolderId, contentFileName, "The quick brown fox jumps over the lazy dog.").getId();
// Add a secondary parent for content file
Node n = new Node();
n.setName("secondary folder " + RUNID);
n.setNodeType(TYPE_CM_FOLDER);
n.setAspectNames(Arrays.asList(ASPECT_CM_PREFERENCES));
HttpResponse response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(n), 201);
String secondaryFolderId = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class).getId();
AssocChild secChild = new AssocChild(contentId, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(secondaryFolderId), toJsonAsStringNonNull(secChild), 201);
Paging paging = getPaging(0, 100);
// Order by folders and modified date first
Map orderBy = Collections.singletonMap("orderBy", "isFolder DESC,modifiedAt DESC");
// Retrieve the node via the primary parent
String primaryChildrenUrl = getNodeChildrenUrl(primaryFolderId);
response = getAll(primaryChildrenUrl, paging, orderBy, 200);
List nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Document.class);
Document node = nodes.get(0);
assertEquals(contentId, node.getId());
assertEquals(primaryFolderId, node.getParentId());
// Retrieve the node via the secondary parent
String secondaryChildrenUrl = getNodeChildrenUrl(secondaryFolderId);
response = getAll(secondaryChildrenUrl, paging, orderBy, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Document.class);
node = nodes.get(0);
assertEquals(contentId, node.getId());
// The parent ID must STILL be the primary parent, even when the info
// is retrieved via the secondary parent.
assertEquals(primaryFolderId, node.getParentId());
}
/**
* Test ability to delete a node with associations (to and from the node) and then restore it.
* Only the primary parent/child assoc(s) for the deleted node(s) is/are restored.
*
* @throws Exception
*/
@Test
public void testDeleteAndRestoreNodeWithAssocs() throws Exception
{
// as user 1 ...
setRequestContext(user1);
String f1Id = null;
String f2Id = null;
String f3Id = null;
try
{
String myFolderNodeId = getMyNodeId();
// create primary parent-child hierarchy
f1Id = createFolder(myFolderNodeId, "f1").getId();
String f1bId = createFolder(f1Id, "f1b").getId();
String f1cId = createFolder(f1bId, "f1c").getId();
String f1dId = createFolder(f1cId, "f1d").getId();
String c1eId = createTextFile(f1dId, "c1e", "some text content").getId();
f2Id = createFolder(myFolderNodeId, "f2").getId();
f3Id = createFolder(myFolderNodeId, "f3").getId();
HttpResponse response = getAll(getNodeParentsUrl(f1bId), null, null, 200);
List nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(f1Id, nodes.get(0).getId());
response = getAll(getNodeParentsUrl(f1dId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(f1cId, nodes.get(0).getId());
response = getAll(getNodeSourcesUrl(c1eId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
// add some secondary parent/child assocs outside of the hierarchy
AssocChild secChild = new AssocChild(f1bId, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(f2Id), toJsonAsStringNonNull(secChild), 201);
secChild = new AssocChild(f1bId, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(f3Id), toJsonAsStringNonNull(secChild), 201);
secChild = new AssocChild(f1dId, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(f2Id), toJsonAsStringNonNull(secChild), 201);
secChild = new AssocChild(f1dId, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(f3Id), toJsonAsStringNonNull(secChild), 201);
// also add a secondary parent/child assoc within the hierarchy
secChild = new AssocChild(f1dId, ASSOC_TYPE_CM_CONTAINS);
post(getNodeSecondaryChildrenUrl(f1bId), toJsonAsStringNonNull(secChild), 201);
// add some peer assocs outside of the hierarchy
AssocTarget tgt = new AssocTarget(c1eId, ASSOC_TYPE_CM_REFERENCES);
post(getNodeTargetsUrl(f2Id), toJsonAsStringNonNull(tgt), 201);
tgt = new AssocTarget(c1eId, ASSOC_TYPE_CM_PARTS);
post(getNodeTargetsUrl(f3Id), toJsonAsStringNonNull(tgt), 201);
// also add a peer assoc within the hierarchy
tgt = new AssocTarget(c1eId, ASSOC_TYPE_CM_PARTS);
post(getNodeTargetsUrl(f1cId), toJsonAsStringNonNull(tgt), 201);
// double-check the secondary parent/child assocs
response = getAll(getNodeParentsUrl(f1bId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(3, nodes.size());
response = getAll(getNodeParentsUrl(f1dId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(4, nodes.size());
response = getAll(getNodeSecondaryChildrenUrl(f2Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(2, nodes.size());
List nodeIds = Arrays.asList(new String[]{nodes.get(0).getId(), nodes.get(1).getId()});
assertTrue(nodeIds.contains(f1bId));
assertTrue(nodeIds.contains(f1dId));
response = getAll(getNodeSecondaryChildrenUrl(f3Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(2, nodes.size());
nodeIds = Arrays.asList(new String[]{nodes.get(0).getId(), nodes.get(1).getId()});
assertTrue(nodeIds.contains(f1bId));
assertTrue(nodeIds.contains(f1dId));
response = getAll(getNodeSecondaryChildrenUrl(f1bId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(f1dId, nodes.get(0).getId());
// double-check the peer assocs
response = getAll(getNodeSourcesUrl(c1eId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(3, nodes.size());
response = getAll(getNodeTargetsUrl(f2Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(c1eId, nodes.get(0).getId());
response = getAll(getNodeTargetsUrl(f3Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(c1eId, nodes.get(0).getId());
response = getAll(getNodeTargetsUrl(f1cId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(c1eId, nodes.get(0).getId());
// ... delete to trashcan/archive ...
deleteNode(f1bId);
getSingle(NodesEntityResource.class, f1bId, null, 404);
response = getAll(getNodeTargetsUrl(f2Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
response = getAll(getNodeTargetsUrl(f3Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
// ... and then restore again ...
post(URL_DELETED_NODES+"/"+f1bId+"/restore", null, null, 200);
// check primary parent-child hierarchy is restored
// but not the secondary parents or peer assocs of the deleted nodes (outside or within the hierarchy)
response = getSingle(NodesEntityResource.class, f1bId, null, 200);
Node nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class);
assertEquals(f1Id, nodeResp.getParentId());
response = getSingle(NodesEntityResource.class, f1cId, null, 200);
nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class);
assertEquals(f1bId, nodeResp.getParentId());
response = getSingle(NodesEntityResource.class, f1dId, null, 200);
nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class);
assertEquals(f1cId, nodeResp.getParentId());
// secondary child assocs have not been restored
response = getAll(getNodeParentsUrl(f1bId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(f1Id, nodes.get(0).getId());
response = getAll(getNodeParentsUrl(f1cId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(f1bId, nodes.get(0).getId());
response = getAll(getNodeParentsUrl(f1dId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(f1cId, nodes.get(0).getId());
response = getAll(getNodeSecondaryChildrenUrl(f2Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
response = getAll(getNodeSecondaryChildrenUrl(f3Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
// peer assocs have not been restored
response = getAll(getNodeSourcesUrl(c1eId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
response = getAll(getNodeTargetsUrl(f1cId), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
response = getAll(getNodeTargetsUrl(f2Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
response = getAll(getNodeTargetsUrl(f3Id), null, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(0, nodes.size());
}
finally
{
// some cleanup
setRequestContext(user1);
if (f1Id != null)
{
deleteNode(f1Id, true, 204);
}
if (f2Id != null)
{
deleteNode(f2Id, true, 204);
}
if (f3Id != null)
{
deleteNode(f3Id, true, 204);
}
}
}
/**
* Test ability to create a node and optionally specify one or more associations (to other existing nodes) at time of create.
*
* @throws Exception
*/
@Test
public void testCreateNodeWithAssocs() throws Exception
{
// as user 1
setRequestContext(user1);
String myFolderNodeId = getMyNodeId();
// create node with some assocs in a single call
// create folder
Node n = new Node();
n.setName("f1");
n.setNodeType(TYPE_CM_FOLDER);
n.setAspectNames(Arrays.asList(ASPECT_CM_PREFERENCES));
HttpResponse response = post(getNodeChildrenUrl(myFolderNodeId), toJsonAsStringNonNull(n), 201);
String f1Id = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class).getId();
// create content node
String o1Name = "o1";
n = new Node();
n.setName(o1Name);
n.setNodeType(TYPE_CM_CONTENT);
response = post(getNodeChildrenUrl(f1Id), toJsonAsStringNonNull(n), 201);
String o1Id = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class).getId();
String o2Name = "o2";
n = new Node();
n.setName(o2Name);
n.setNodeType(TYPE_CM_CONTENT);
response = post(getNodeChildrenUrl(f1Id), toJsonAsStringNonNull(n), 201);
String o2Id = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class).getId();
// create folder node with some assocs
String f2Name = "f2";
n = new Node();
n.setName(f2Name);
n.setNodeType(TYPE_CM_FOLDER);
AssocChild secChild = new AssocChild(o1Id, ASSOC_TYPE_CM_CONTAINS);
n.setSecondaryChildren(Collections.singletonList(secChild));
AssocTarget tgt = new AssocTarget(o2Id, ASSOC_TYPE_CM_REFERENCES);
n.setTargets(Collections.singletonList(tgt));
response = post(getNodeChildrenUrl(myFolderNodeId), toJsonAsStringNonNull(n), 201);
String f2Id = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class).getId();
String f3Id = createFolder(myFolderNodeId, "f3").getId();
try
{
Paging paging = getPaging(0, 100);
response = getAll(getNodeSecondaryChildrenUrl(f2Id), paging, null, 200);
List nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(o1Id, nodes.get(0).getId());
response = getAll(getNodeTargetsUrl(f2Id), paging, null, 200);
nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, nodes.size());
assertEquals(o2Id, nodes.get(0).getId());
// TODO test model with mandatory aspect
// -ve test - minor: error code if creating a cyclic child assoc (REPO-475)
n = new Node();
n.setName("my-folder");
n.setNodeType(TYPE_CM_FOLDER);
AssocChild assocChild = new AssocChild(myFolderNodeId, "cm:contains");
n.setSecondaryChildren(Collections.singletonList(assocChild));
post(getNodeChildrenUrl(myFolderNodeId), RestApiUtil.toJsonAsStringNonNull(n), 400);
// -ve tests - missing targetId / childId or assocType
n = new Node();
n.setName("my-folder");
n.setNodeType(TYPE_CM_FOLDER);
assocChild = new AssocChild(null, ASSOC_TYPE_CM_CONTAINS);
n.setSecondaryChildren(Collections.singletonList(assocChild));
post(getNodeChildrenUrl(f3Id), RestApiUtil.toJsonAsStringNonNull(n), 400);
n = new Node();
n.setName("my-folder");
n.setNodeType(TYPE_CM_FOLDER);
assocChild = new AssocChild(f2Id, null);
n.setSecondaryChildren(Collections.singletonList(assocChild));
post(getNodeChildrenUrl(f3Id), RestApiUtil.toJsonAsStringNonNull(n), 400);
n = new Node();
n.setName("my-folder");
n.setNodeType(TYPE_CM_FOLDER);
tgt = new AssocTarget(null, ASSOC_TYPE_CM_REFERENCES);
n.setTargets(Collections.singletonList(tgt));
post(getNodeChildrenUrl(f3Id), RestApiUtil.toJsonAsStringNonNull(n), 400);
n = new Node();
n.setName("my-folder");
n.setNodeType(TYPE_CM_FOLDER);
tgt = new AssocTarget(f2Id, null);
n.setTargets(Collections.singletonList(tgt));
post(getNodeChildrenUrl(f3Id), RestApiUtil.toJsonAsStringNonNull(n), 400);
}
finally
{
// some cleanup
setRequestContext(user1);
deleteNode(f1Id, true, 204);
deleteNode(f2Id, true, 204);
deleteNode(f3Id, true, 204);
}
}
@Override
public String getScope()
{
return "public";
}
}