/* * 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 . */ 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.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.alfresco.model.ContentModel; import org.alfresco.model.ForumModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.model.Repository; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.rest.api.Nodes; 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.Folder; import org.alfresco.rest.api.tests.client.data.Node; import org.alfresco.rest.api.tests.client.data.PathInfo; import org.alfresco.rest.api.tests.client.data.PathInfo.ElementInfo; import org.alfresco.rest.api.tests.client.data.UserInfo; import org.alfresco.rest.api.nodes.NodesEntityResource; import org.alfresco.rest.api.tests.RepoService.TestNetwork; import org.alfresco.rest.api.tests.RepoService.TestPerson; import org.alfresco.rest.api.tests.RepoService.TestSite; 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.Paging; import org.alfresco.rest.api.tests.client.data.SiteRole; 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.framework.jacksonextensions.JacksonHelper; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteVisibility; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.util.ResourceUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Serializable; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; /** * API tests for: * * * @author Jamal Kaabi-Mofrad * @author janv */ public class NodeApiTest extends AbstractBaseApiTest { private static final String RESOURCE_PREFIX = "publicapi/upload/"; /** * User one from network one */ private TestPerson userOneN1; /** * User two from network one */ private TestPerson userTwoN1; /** * Private site of user one from network one */ private TestSite userOneN1Site; private String user1; private String user2; private List users = new ArrayList<>(); protected MutableAuthenticationService authenticationService; protected PersonService personService; protected Repository repositoryHelper; protected JacksonUtil jacksonUtil; protected PermissionService permissionService; @Before public void setup() throws Exception { authenticationService = applicationContext.getBean("authenticationService", MutableAuthenticationService.class); personService = applicationContext.getBean("personService", PersonService.class); repositoryHelper = applicationContext.getBean("repositoryHelper", Repository.class); jacksonUtil = new JacksonUtil(applicationContext.getBean("jsonHelper", JacksonHelper.class)); permissionService = applicationContext.getBean("permissionService", PermissionService.class); user1 = createUser("user1" + System.currentTimeMillis()); user2 = createUser("user2" + System.currentTimeMillis()); // We just need to clean the on-premise-users, // so the tests for the specific network would work. users.add(user1); users.add(user2); TestNetwork networkOne = getTestFixture().getRandomNetwork(); userOneN1 = networkOne.createUser(); userTwoN1 = networkOne.createUser(); userOneN1Site = createSite(networkOne, userOneN1, SiteVisibility.PRIVATE); } @After public void tearDown() throws Exception { AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); for (final String user : users) { transactionHelper.doInTransaction(new RetryingTransactionCallback() { @Override public Void execute() throws Throwable { if (personService.personExists(user)) { authenticationService.deleteAuthentication(user); personService.deletePerson(user); } return null; } }); } users.clear(); AuthenticationUtil.clearCurrentSecurityContext(); } /** * Tests get document library children. *

GET:

* {@literal :/alfresco/api//public/alfresco/versions/1/nodes//children} */ @Test public void testListDocLibChildren() throws Exception { AuthenticationUtil.setFullyAuthenticatedUser(userOneN1.getId()); NodeRef docLibNodeRef = userOneN1Site.getContainerNodeRef(("documentLibrary")); String folder1 = "folder" + System.currentTimeMillis() + "_1"; repoService.addToDocumentLibrary(userOneN1Site, folder1, ContentModel.TYPE_FOLDER); String folder2 = "folder" + System.currentTimeMillis() + "_2"; repoService.addToDocumentLibrary(userOneN1Site, folder2, ContentModel.TYPE_FOLDER); String content1 = "content" + System.currentTimeMillis() + "_1"; repoService.addToDocumentLibrary(userOneN1Site, content1, ContentModel.TYPE_CONTENT); String content2 = "content" + System.currentTimeMillis() + "_2"; repoService.addToDocumentLibrary(userOneN1Site, content2, ContentModel.TYPE_CONTENT); String forum1 = "forum" + System.currentTimeMillis() + "_1"; repoService.createObjectOfCustomType(docLibNodeRef, forum1, ForumModel.TYPE_TOPIC.toString()); Paging paging = getPaging(0, 100); HttpResponse response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, 200); List nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class); assertEquals(4, nodes.size()); // forum is part of the default ignored types // Paging ExpectedPaging expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse()); assertEquals(4, expectedPaging.getCount().intValue()); assertEquals(0, expectedPaging.getSkipCount().intValue()); assertEquals(100, expectedPaging.getMaxItems().intValue()); assertFalse(expectedPaging.getHasMoreItems().booleanValue()); // Order by folders and modified date first Map orderBy = Collections.singletonMap("orderBy", "isFolder DESC,modifiedAt DESC"); response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200); nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class); assertEquals(4, nodes.size()); assertEquals(folder2, nodes.get(0).getName()); assertTrue(nodes.get(0).getIsFolder()); assertEquals(folder1, nodes.get(1).getName()); assertTrue(nodes.get(1).getIsFolder()); assertEquals(content2, nodes.get(2).getName()); assertFalse(nodes.get(2).getIsFolder()); assertEquals(content1, nodes.get(3).getName()); assertFalse(nodes.get(3).getIsFolder()); // Order by folders last and modified date first orderBy = Collections.singletonMap("orderBy", "isFolder ASC,modifiedAt DESC"); response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200); nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class); assertEquals(4, nodes.size()); assertEquals(content2, nodes.get(0).getName()); assertEquals(content1, nodes.get(1).getName()); assertEquals(folder2, nodes.get(2).getName()); assertEquals(folder1, nodes.get(3).getName()); // Order by folders and modified date last orderBy = Collections.singletonMap("orderBy", "isFolder,modifiedAt"); response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200); nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class); assertEquals(4, nodes.size()); assertEquals(content1, nodes.get(0).getName()); assertEquals(content2, nodes.get(1).getName()); assertEquals(folder1, nodes.get(2).getName()); assertEquals(folder2, nodes.get(3).getName()); // Order by folders and modified date first orderBy = Collections.singletonMap("orderBy", "isFolder DESC,modifiedAt DESC"); // SkipCount=0,MaxItems=2 paging = getPaging(0, 2); response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200); nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class); assertEquals(2, nodes.size()); assertEquals(folder2, nodes.get(0).getName()); assertEquals(folder1, nodes.get(1).getName()); expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse()); assertEquals(2, expectedPaging.getCount().intValue()); assertEquals(0, expectedPaging.getSkipCount().intValue()); assertEquals(2, expectedPaging.getMaxItems().intValue()); assertTrue(expectedPaging.getHasMoreItems().booleanValue()); // SkipCount=2,MaxItems=4 paging = getPaging(2, 4); response = getAll(getChildrenUrl(docLibNodeRef), userOneN1.getId(), paging, orderBy, 200); nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Node.class); assertEquals(2, nodes.size()); assertEquals(content2, nodes.get(0).getName()); assertEquals(content1, nodes.get(1).getName()); expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse()); assertEquals(2, expectedPaging.getCount().intValue()); assertEquals(2, expectedPaging.getSkipCount().intValue()); assertEquals(4, expectedPaging.getMaxItems().intValue()); assertFalse(expectedPaging.getHasMoreItems().booleanValue()); // userTwoN1 tries to access userOneN1's docLib AuthenticationUtil.setFullyAuthenticatedUser(userTwoN1.getId()); paging = getPaging(0, Integer.MAX_VALUE); getAll(getChildrenUrl(docLibNodeRef), userTwoN1.getId(), paging, 403); } /** * Tests get user's home children. *

GET:

* {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes//children} */ @Test public void testListMyFilesChildren() throws Exception { AuthenticationUtil.setFullyAuthenticatedUser(user1); NodeRef myFilesNodeRef = repositoryHelper.getUserHome(personService.getPerson(user1)); String folder1 = "folder" + System.currentTimeMillis() + "_1"; repoService.createFolder(myFilesNodeRef, folder1); String folder2 = "folder" + System.currentTimeMillis() + "_2"; repoService.createFolder(myFilesNodeRef, folder2); String content1 = "content" + System.currentTimeMillis() + "_1"; NodeRef contentNodeRef = repoService.createDocument(myFilesNodeRef, content1, "The quick brown fox jumps over the lazy dog."); repoService.getNodeService().setProperty(contentNodeRef, ContentModel.PROP_OWNER, user1); repoService.getNodeService().setProperty(contentNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA, (Serializable) Collections.singletonList("doclib:1444660852296")); Paging paging = getPaging(0, Integer.MAX_VALUE); HttpResponse response = getAll(getChildrenUrl(myFilesNodeRef), user1, paging, 200); List nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Document.class); assertEquals(3, nodes.size()); // Order by folders and modified date first Map orderBy = Collections.singletonMap("orderBy", "isFolder DESC,modifiedAt DESC"); response = getAll(getChildrenUrl(myFilesNodeRef), user1, paging, orderBy, 200); nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Document.class); assertEquals(3, nodes.size()); assertEquals(folder2, nodes.get(0).getName()); assertEquals(folder1, nodes.get(1).getName()); Document node = (Document) nodes.get(2); assertEquals(content1, node.getName()); assertEquals("cm:content", node.getNodeType()); assertEquals(contentNodeRef.getId(), node.getId()); UserInfo createdByUser = node.getCreatedByUser(); assertEquals(user1, createdByUser.getUserName()); assertEquals(user1 + " " + user1, createdByUser.getDisplayName()); UserInfo modifiedByUser = node.getModifiedByUser(); assertEquals(user1, modifiedByUser.getUserName()); assertEquals(user1 + " " + user1, modifiedByUser.getDisplayName()); assertEquals(MimetypeMap.MIMETYPE_BINARY, node.getContent().getMimeType()); assertNotNull(node.getContent().getMimeTypeName()); assertNotNull(node.getContent().getEncoding()); assertTrue(node.getContent().getSizeInBytes() > 0); // Invalid QName (Namespace prefix cm... is not mapped to a namespace URI) for the orderBy parameter. orderBy = Collections.singletonMap("orderBy", "isFolder DESC,cm" + System.currentTimeMillis() + ":modified DESC"); getAll(getChildrenUrl(myFilesNodeRef), user1, paging, orderBy, 400); AuthenticationUtil.setFullyAuthenticatedUser(user2); // user2 tries to access user1's home folder getAll(getChildrenUrl(myFilesNodeRef), user2, paging, 403); AuthenticationUtil.setFullyAuthenticatedUser(user1); // request property via select Map params = new LinkedHashMap<>(); params.put("select", "cm_lastThumbnailModification");// TODO replace the underscore with colon when the framework is fixed. params.put("orderBy", "isFolder DESC,modifiedAt DESC"); response = getAll(getChildrenUrl(myFilesNodeRef), user1, paging, params, 200); nodes = jacksonUtil.parseEntries(response.getJsonResponse(), Document.class); assertEquals(3, nodes.size()); assertNull("There shouldn't be a 'properties' object in the response.", nodes.get(0).getProperties()); assertNull("There shouldn't be a 'properties' object in the response.", nodes.get(1).getProperties()); assertNotNull("There should be a 'properties' object in the response.", nodes.get(2).getProperties()); Set> props = nodes.get(2).getProperties().entrySet(); assertEquals(1, props.size()); Entry entry = props.iterator().next(); assertEquals("cm:lastThumbnailModification", entry.getKey()); assertEquals("doclib:1444660852296", ((List) entry.getValue()).get(0)); } /** * Tests get node with path information. *

GET:

* {@literal :/alfresco/api//public/alfresco/versions/1/nodes/?select=path} */ @Test public void testGetPathElements_DocLib() throws Exception { AuthenticationUtil.setFullyAuthenticatedUser(userOneN1.getId()); userOneN1Site.inviteToSite(userTwoN1.getEmail(), SiteRole.SiteConsumer); NodeRef docLibNodeRef = userOneN1Site.getContainerNodeRef(("documentLibrary")); // /Company Home/Sites/RandomSite/documentLibrary/folder_A String folderA = "folder" + System.currentTimeMillis() + "_A"; NodeRef folderA_Ref = repoService.createFolder(docLibNodeRef, folderA); // /Company Home/Sites/RandomSite/documentLibrary/folder_A/folder_B String folderB = "folder" + System.currentTimeMillis() + "_B"; NodeRef folderB_Ref = repoService.createFolder(folderA_Ref, folderB); // /Company Home/Sites/RandomSite/documentLibrary/folder_A/folder_B/folder_C String folderC = "folder" + System.currentTimeMillis() + "_C"; NodeRef folderC_Ref = repoService.createFolder(folderB_Ref, folderC); // /Company Home/Sites/RandomSite/documentLibrary/folder_A/folder_B/folder_C/content String content = "content" + System.currentTimeMillis(); NodeRef contentNodeRef = repoService.createDocument(folderC_Ref, content, "The quick brown fox jumps over the lazy dog."); // Revoke folderB inherited permissions permissionService.setInheritParentPermissions(folderB_Ref, false); // Grant userTwoN1 permission for folderC permissionService.setPermission(folderC_Ref, userTwoN1.getId(), PermissionService.CONSUMER, true); //...nodes/nodeId?select=path Map params = Collections.singletonMap("select", "path"); HttpResponse response = getSingle(NodesEntityResource.class, userOneN1.getId(), contentNodeRef.getId(), params, 200); Document node = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class); PathInfo path = node.getPath(); assertNotNull(path); assertTrue(path.getIsComplete()); assertNotNull(path.getName()); // the path should only include the parents (not the requested node) assertFalse(path.getName().endsWith(content)); assertTrue(path.getName().startsWith("/Company Home")); List pathElements = path.getElements(); assertEquals(7, pathElements.size()); assertEquals("Company Home", pathElements.get(0).getName()); assertEquals("Sites", pathElements.get(1).getName()); assertEquals(userOneN1Site.getSiteId(), pathElements.get(2).getName()); assertEquals("documentLibrary", pathElements.get(3).getName()); assertEquals(folderA, pathElements.get(4).getName()); assertEquals(folderB, pathElements.get(5).getName()); assertEquals(folderC, pathElements.get(6).getName()); // Try the above tests with userTwoN1 (site consumer) AuthenticationUtil.setFullyAuthenticatedUser(userTwoN1.getId()); response = getSingle(NodesEntityResource.class, userTwoN1.getId(), contentNodeRef.getId(), params, 200); node = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class); path = node.getPath(); assertNotNull(path); assertFalse("The path is not complete as the user doesn't have permission to access the full path.", path.getIsComplete()); assertNotNull(path.getName()); // site consumer (userTwoN1) dose not have access to the folderB assertFalse("site consumer (userTwoN1) dose not have access to the folderB", path.getName().contains(folderB)); assertFalse(path.getName().startsWith("/Company Home")); // Go up as far as they can, before getting access denied (i.e. "/folderC") assertTrue(path.getName().endsWith(folderC)); pathElements = path.getElements(); assertEquals(1, pathElements.size()); assertEquals(folderC, pathElements.get(0).getName()); } /** * Tests get node with path information. *

GET:

* {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes/?select=path} */ @Test public void testGetPathElements_MyFiles() throws Exception { AuthenticationUtil.setFullyAuthenticatedUser(user1); HttpResponse response = getSingle(NodesEntityResource.class, user1, Nodes.PATH_MY, null, 200); Node node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class); String myFilesNodeId = node.getId(); assertNotNull(myFilesNodeId); assertEquals(user1, node.getName()); assertTrue(node.getIsFolder()); // /Company Home/User Homes/user/folder_A String folderA = "folder" + System.currentTimeMillis() + "_A"; NodeRef folderA_Ref = repoService.createFolder(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, myFilesNodeId), folderA); // /Company Home/User Homes/user/folder_A/folder_B String folderB = "folder" + System.currentTimeMillis() + "_B"; NodeRef folderB_Ref = repoService.createFolder(folderA_Ref, folderB); // /Company Home/User Homes/user/folder_A/folder_B/folder_C String folderC = "folder" + System.currentTimeMillis() + "_C"; NodeRef folderC_Ref = repoService.createFolder(folderB_Ref, folderC); //...nodes/nodeId?select=pathInfo Map params = Collections.singletonMap("select", "path"); response = getSingle(NodesEntityResource.class, user1, folderC_Ref.getId(), params, 200); node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class); PathInfo pathInfo = node.getPath(); assertNotNull(pathInfo); assertTrue(pathInfo.getIsComplete()); assertNotNull(pathInfo.getName()); // the pathInfo should only include the parents (not the requested node) assertFalse(pathInfo.getName().endsWith(folderC)); assertTrue(pathInfo.getName().startsWith("/Company Home")); List pathElements = pathInfo.getElements(); assertEquals(5, pathElements.size()); assertEquals("Company Home", pathElements.get(0).getName()); assertNotNull(pathElements.get(0).getId()); assertEquals("User Homes", pathElements.get(1).getName()); assertNotNull(pathElements.get(1).getId()); assertEquals(user1, pathElements.get(2).getName()); assertNotNull(pathElements.get(2).getId()); assertEquals(folderA, pathElements.get(3).getName()); assertNotNull(pathElements.get(3).getId()); assertEquals(folderB, pathElements.get(4).getName()); assertNotNull(pathElements.get(4).getId()); } /** * Tests well-known aliases. *

GET:

*
    *
  • {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-}
  • *
  • {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes/-my-}
  • *
  • {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes/-shared-}
  • *
*/ @Test public void testGetNodeWithKnownAlias() throws Exception { HttpResponse response = getSingle(NodesEntityResource.class, user1, Nodes.PATH_ROOT, null, 200); Node node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class); assertEquals("Company Home", node.getName()); assertNotNull(node.getId()); assertNull(node.getPath()); // unknown alias getSingle(NodesEntityResource.class, user1, "testSomeUndefinedAlias", null, 404); response = getSingle(NodesEntityResource.class, user1, Nodes.PATH_MY, null, 200); node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class); String myFilesNodeId = node.getId(); assertNotNull(myFilesNodeId); assertEquals(user1, node.getName()); assertTrue(node.getIsFolder()); assertNull(node.getPath()); // note: path can be optionally "select"'ed - see separate test response = getSingle(NodesEntityResource.class, user1, Nodes.PATH_SHARED, null, 200); node = jacksonUtil.parseEntry(response.getJsonResponse(), Node.class); String sharedFilesNodeId = node.getId(); assertNotNull(sharedFilesNodeId); assertEquals("Shared", node.getName()); assertTrue(node.getIsFolder()); assertNull(node.getPath()); //Delete user1's home AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); repoService.getNodeService().deleteNode(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, myFilesNodeId)); AuthenticationUtil.setFullyAuthenticatedUser(user1); getSingle(NodesEntityResource.class, user1, Nodes.PATH_MY, null, 404); // Not found } /** * Tests Multipart upload to user's home (a.k.a My Files). *

POST:

* {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes//children} */ @Test public void testUploadToMyFiles() throws Exception { final String fileName = "quick.pdf"; final File file = getResourceFile(fileName); Paging paging = getPaging(0, Integer.MAX_VALUE); HttpResponse response = getAll(getChildrenUrl(Nodes.PATH_MY), 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 into a non-existent folder post(getChildrenUrl(UUID.randomUUID().toString()), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 404); // Upload response = post(getChildrenUrl(Nodes.PATH_MY), 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.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(Nodes.PATH_MY), 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(Nodes.PATH_MY), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 409); response = getAll(getChildrenUrl(Nodes.PATH_MY), 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()); // Set autoRename=true and upload the same file again reqBody = MultiPartBuilder.copy(multiPartBuilder) .setAutoRename(true) .build(); response = post(getChildrenUrl(Nodes.PATH_MY), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 201); document = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class); // Check the upload response assertEquals("quick-1.pdf", document.getName()); // upload the same file again response = post(getChildrenUrl(Nodes.PATH_MY), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 201); document = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class); // Check the upload response assertEquals("quick-2.pdf", document.getName()); response = getAll(getChildrenUrl(Nodes.PATH_MY), user1, paging, 200); pagingResult = parsePaging(response.getJsonResponse()); assertNotNull(paging); assertEquals(numOfNodes + 3, pagingResult.getCount().intValue()); // User2 tries to upload a new file into the user1's home folder. response = getSingle(NodesEntityResource.class, user1, Nodes.PATH_MY, 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.getId()), user2, new String(reqBody.getBody()), null, reqBody.getContentType(), 403); response = getAll(getChildrenUrl(Nodes.PATH_MY), user1, paging, 200); pagingResult = parsePaging(response.getJsonResponse()); assertNotNull(paging); assertEquals("Access Denied. The file shouldn't have been uploaded.", numOfNodes + 3, pagingResult.getCount().intValue()); // User1 tries to upload a file into a document rather than a folder! post(getChildrenUrl(document.getId()), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 400); // Try to upload a file without defining the required formData reqBody = MultiPartBuilder.create().setAutoRename(true).build(); post(getChildrenUrl(Nodes.PATH_MY), user1, new String(reqBody.getBody()), null, reqBody.getContentType(), 400); } /** * Tests Multipart upload to a Site. *

POST:

* {@literal :/alfresco/api//public/alfresco/versions/1/nodes//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.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); 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); } /** * Tests delete (folder or file). *

DELETE:

* {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes/} */ @Test public void testDelete() throws Exception { AuthenticationUtil.setFullyAuthenticatedUser(user1); NodeRef myFilesNodeRef = repositoryHelper.getUserHome(personService.getPerson(user1)); String content1 = "content" + System.currentTimeMillis() + "_1"; NodeRef content1Ref = repoService.createDocument(myFilesNodeRef, content1, "The quick brown fox jumps over the lazy dog."); // delete file delete("nodes", user1, content1Ref.getId(), 204); // -ve test delete("nodes", user1, content1Ref.getId(), 404); String folder1 = "folder" + System.currentTimeMillis() + "_1"; NodeRef folder1Ref = repoService.createFolder(myFilesNodeRef, folder1); String folder2 = "folder" + System.currentTimeMillis() + "_2"; NodeRef folder2Ref = repoService.createFolder(folder1Ref, folder2); String content2 = "content" + System.currentTimeMillis() + "_2"; NodeRef content2Ref = repoService.createDocument(folder2Ref, content2, "The quick brown fox jumps over the lazy dog."); // cascade delete folder delete("nodes", user1, folder1Ref.getId(), 204); // -ve test delete("nodes", user1, folder2Ref.getId(), 404); delete("nodes", user1, content2Ref.getId(), 404); // -ve test NodeRef chNodeRef = repositoryHelper.getCompanyHome(); delete("nodes", user1, chNodeRef.getId(), 403); } /** * Tests create folder. *

POST:

* {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes//children} */ @Test public void testCreateFolder() throws Exception { AuthenticationUtil.setFullyAuthenticatedUser(user1); NodeRef personNodeRef = personService.getPerson(user1); NodeRef myFilesNodeRef = repositoryHelper.getUserHome(personNodeRef); UserInfo expectedUser = new UserInfo(user1, user1+" "+user1); String postUrl = "nodes/"+myFilesNodeRef.getId()+"/children"; Folder f1 = new Folder(); f1.setName("f1"); f1.setNodeType("cm:folder"); // create folder HttpResponse response = post(postUrl, user1, toJsonAsStringNonNull(f1), 201); Folder folderResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Folder.class); f1.setIsFolder(true); f1.setParentId(myFilesNodeRef.getId()); f1.setAspectNames(Collections.singletonList("cm:auditable")); f1.setCreatedByUser(expectedUser); f1.setModifiedByUser(expectedUser); f1.expected(folderResp); // create folder with properties Map props = new HashMap<>(); props.put("cm:title","my folder title"); props.put("cm:description","my folder description"); Folder f2 = new Folder(); f2.setName("f2"); f2.setNodeType("cm:folder"); f2.setProperties(props); response = post(postUrl, user1, toJsonAsStringNonNull(f2), 201); folderResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Folder.class); f2.setIsFolder(true); f2.setParentId(myFilesNodeRef.getId()); f2.setAspectNames(Arrays.asList("cm:auditable","cm:titled")); f2.setCreatedByUser(expectedUser); f2.setModifiedByUser(expectedUser); f2.expected(folderResp); // -ve test - name is mandatory Folder invalid = new Folder(); invalid.setNodeType("cm:folder"); post(postUrl, user1, toJsonAsStringNonNull(invalid), 400); // -ve test - node type is mandatory invalid = new Folder(); invalid.setName("my folder"); post(postUrl, user1, toJsonAsStringNonNull(invalid), 400); // -ve test - invalid (eg. not a folder) parent id Folder f3 = new Folder(); f3.setName("f3"); f3.setNodeType("cm:folder"); post("nodes/"+personNodeRef.getId()+"/children", user1, toJsonAsStringNonNull(f3), 400); // -ve test - unknown parent folder node id post("nodes/"+UUID.randomUUID().toString()+"/children", user1, toJsonAsStringNonNull(f3), 404); // -ve test - duplicate name post(postUrl, user1, toJsonAsStringNonNull(f1), 409); } /** * Tests create empty file. *

POST:

* {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes//children} */ @Test public void testCreateEmptyFile() throws Exception { AuthenticationUtil.setFullyAuthenticatedUser(user1); NodeRef personNodeRef = personService.getPerson(user1); NodeRef myFilesNodeRef = repositoryHelper.getUserHome(personNodeRef); UserInfo expectedUser = new UserInfo(user1, user1+" "+user1); String postUrl = "nodes/"+myFilesNodeRef.getId()+"/children"; Document d1 = new Document(); d1.setName("d1.txt"); d1.setNodeType("cm:content"); ContentInfo ci = new ContentInfo(); ci.setMimeType("text/plain"); d1.setContent(ci); // create empty file HttpResponse response = post(postUrl, user1, toJsonAsStringNonNull(d1), 201); Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); d1.setIsFolder(false); d1.setParentId(myFilesNodeRef.getId()); d1.setAspectNames(Collections.singletonList("cm:auditable")); d1.setCreatedByUser(expectedUser); d1.setModifiedByUser(expectedUser); d1.getContent().setMimeTypeName("Plain Text"); d1.getContent().setSizeInBytes(0L); d1.getContent().setEncoding("UTF-8"); d1.expected(documentResp); // create empty file with properties Map props = new HashMap<>(); props.put("cm:title","my file title"); props.put("cm:description","my file description"); Document d2 = new Document(); d2.setName("d2.txt"); d2.setNodeType("cm:content"); d2.setProperties(props); response = post(postUrl, user1, toJsonAsStringNonNull(d2), 201); documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); d2.setIsFolder(false); d2.setParentId(myFilesNodeRef.getId()); d2.setAspectNames(Arrays.asList("cm:auditable","cm:titled")); d2.setCreatedByUser(expectedUser); d2.setModifiedByUser(expectedUser); ContentInfo ciExpected = new ContentInfo(); ciExpected.setMimeType("text/plain"); ciExpected.setMimeTypeName("Plain Text"); ciExpected.setSizeInBytes(0L); ciExpected.setEncoding("UTF-8"); d2.setContent(ciExpected); d2.expected(documentResp); // -ve test - name is mandatory Document invalid = new Document(); invalid.setNodeType("cm:content"); post(postUrl, user1, toJsonAsStringNonNull(invalid), 400); // -ve test - node type is mandatory invalid = new Document(); invalid.setName("my file.txt"); post(postUrl, user1, toJsonAsStringNonNull(invalid), 400); // -ve test - invalid (eg. not a folder) parent id Document d3 = new Document(); d3.setName("d3.txt"); d3.setNodeType("cm:content"); post("nodes/"+personNodeRef.getId()+"/children", user1, toJsonAsStringNonNull(d3), 400); // -ve test - unknown parent folder node id post("nodes/"+UUID.randomUUID().toString()+"/children", user1, toJsonAsStringNonNull(d3), 404); // -ve test - duplicate name post(postUrl, user1, toJsonAsStringNonNull(d1), 409); } // TODO add test to create multiple folders & empty files (within same parent folder) // TODO add test for file/folder links - creating, getting, listing, deleting 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 public String getScope() { return "public"; } // TODO move into RestApiUtil (and statically init the OM) private String toJsonAsStringNonNull(Object obj) throws IOException { ObjectMapper om = new ObjectMapper(); om.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL); return om.writeValueAsString(obj); } }