diff --git a/source/test-java/org/alfresco/rest/api/tests/ApiTest.java b/source/test-java/org/alfresco/rest/api/tests/ApiTest.java index 593a89a782..acb80f9e79 100644 --- a/source/test-java/org/alfresco/rest/api/tests/ApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/ApiTest.java @@ -34,11 +34,15 @@ import org.junit.runners.Suite; * Public API tests. * * @author steveglover + * @author janv + * @author Jamal Kaabi-Mofrad + * @author Gethin James * */ @RunWith(Suite.class) @Suite.SuiteClasses({ NodeApiTest.class, + NodeAssociationsApiTest.class, QueriesApiTest.class, RenditionsTest.class, SharedLinkApiTest.class, diff --git a/source/test-java/org/alfresco/rest/api/tests/NodeAssociationsApiTest.java b/source/test-java/org/alfresco/rest/api/tests/NodeAssociationsApiTest.java new file mode 100644 index 0000000000..6c6144fa1c --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/tests/NodeAssociationsApiTest.java @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2005-2016 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 org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.Queries; +import org.alfresco.rest.api.model.AssocTarget; +import org.alfresco.rest.api.tests.client.HttpResponse; +import org.alfresco.rest.api.tests.client.PublicApiClient; +import org.alfresco.rest.api.tests.client.PublicApiClient.Paging; +import org.alfresco.rest.api.tests.client.RequestContext; +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.Tag; +import org.alfresco.rest.api.tests.util.RestApiUtil; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull; +import static org.junit.Assert.*; + +/** + * API tests for Node Associations + * + * Peer Associations (source -> target) + * + * + * TODO - Child Associations (parent -> child) - primary vs secondary + * + * + * + * Note: please also refer to NodeApiTests for specific tests for "managing" primary parent/child association. + + * @author janv + */ +public class NodeAssociationsApiTest extends AbstractBaseApiTest +{ + private static final String URL_TARGETS = "targets"; + private static final String URL_SOURCES = "sources"; + + private static final String ASPECT_CM_REFERENCING = "cm:referencing"; + private static final String ASSOC_TYPE_CM_REFERENCES = "cm:references"; + + private static final String ASPECT_CM_PARTABLE = "cm:partable"; + private static final String ASSOC_TYPE_CM_PARTS = "cm:parts"; + + private static final String PARAM_ASSOC_TYPE = "assocType"; + + private String user1; + private String user2; + private List users = new ArrayList<>(); + + protected MutableAuthenticationService authenticationService; + protected PersonService personService; + + private final String RUNID = System.currentTimeMillis()+""; + + @Before + public void setup() throws Exception + { + authenticationService = applicationContext.getBean("authenticationService", MutableAuthenticationService.class); + personService = applicationContext.getBean("personService", PersonService.class); + + // note: createUser currently relies on repoService + user1 = createUser("user1-" + RUNID); + user2 = createUser("user2-" + RUNID); + + // We just need to clean the on-premise-users, + // so the tests for the specific network would work. + users.add(user1); + users.add(user2); + } + + @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(); + } + + protected String getNodeTargetsUrl(String nodeId) + { + return URL_NODES + "/" + nodeId + "/" + URL_TARGETS; + } + + protected String getNodeSourcesUrl(String nodeId) + { + return URL_NODES + "/" + nodeId + "/" + URL_SOURCES; + } + + /** + * Tests basic api to manage (add, list, remove) peer node associations + * + *

GET:

+ * {@literal :/alfresco/api//public/alfresco/versions/1/queries/live-search-nodes} + */ + @Test + public void testPeerNodeAssocs() throws Exception + { + String myFolderNodeId = getMyNodeId(user1); + + // create folder + String f1Id = createFolder(user1, myFolderNodeId, "f1").getId(); + + // create content node + Node n = new Node(); + n.setName("o1"); + n.setNodeType(TYPE_CM_CONTENT); + n.setAspectNames(Arrays.asList(ASPECT_CM_REFERENCING, ASPECT_CM_PARTABLE)); + HttpResponse response = post(getNodeChildrenUrl(f1Id), user1, toJsonAsStringNonNull(n), 201); + String o1Id = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class).getId(); + + // create ano' folder + String f2Id = createFolder(user1, myFolderNodeId, "f2").getId(); + + // create ano' content node + n = new Node(); + n.setName("o2"); + n.setNodeType(TYPE_CM_CONTENT); + n.setAspectNames(Arrays.asList(ASPECT_CM_REFERENCING, ASPECT_CM_PARTABLE)); + response = post(getNodeChildrenUrl(f2Id), user1, toJsonAsStringNonNull(n), 201); + String o2Id = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class).getId(); + + + try + { + // As user 1 ... + + Paging paging = getPaging(0, 100); + + // -ve test - unauthenticated - belts-and-braces ;-) + getAll(getNodeTargetsUrl(f1Id), null, paging, null, 401); + getAll(getNodeSourcesUrl(f1Id), null, paging, null, 401); + + // empty lists - before + + response = getAll(getNodeTargetsUrl(o1Id), user1, paging, null, 200); + List nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeSourcesUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeTargetsUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeSourcesUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + // -ve test - unauthenticated - belts-and-braces ;-) + AssocTarget tgt = new AssocTarget(o2Id, ASSOC_TYPE_CM_REFERENCES); + post(getNodeTargetsUrl(o1Id), null, toJsonAsStringNonNull(tgt), 401); + + // create two assocs in one direction (from src to tgt) + + tgt = new AssocTarget(o2Id, ASSOC_TYPE_CM_REFERENCES); + post(getNodeTargetsUrl(o1Id), user1, toJsonAsStringNonNull(tgt), 201); + + tgt = new AssocTarget(o2Id, ASSOC_TYPE_CM_PARTS); + post(getNodeTargetsUrl(o1Id), user1, toJsonAsStringNonNull(tgt), 201); + + response = getAll(getNodeTargetsUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + response = getAll(getNodeSourcesUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeTargetsUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeSourcesUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + // create two assocs in the other direction (from tgt to src) + + tgt = new AssocTarget(o1Id, ASSOC_TYPE_CM_REFERENCES); + post(getNodeTargetsUrl(o2Id), user1, toJsonAsStringNonNull(tgt), 201); + + tgt = new AssocTarget(o1Id, ASSOC_TYPE_CM_PARTS); + post(getNodeTargetsUrl(o2Id), user1, toJsonAsStringNonNull(tgt), 201); + + response = getAll(getNodeTargetsUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + response = getAll(getNodeSourcesUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + response = getAll(getNodeTargetsUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + response = getAll(getNodeSourcesUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + // test basic list filter + + Map params = new HashMap<>(); + params.put("where", "(assocType='"+ASSOC_TYPE_CM_REFERENCES+"')"); + + response = getAll(getNodeTargetsUrl(o1Id), user1, paging, params, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(1, nodes.size()); + assertEquals(o2Id, nodes.get(0).getId()); + assertEquals(ASSOC_TYPE_CM_REFERENCES, nodes.get(0).getAssociation().getAssocType()); + + response = getAll(getNodeSourcesUrl(o1Id), user1, paging, params, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(1, nodes.size()); + assertEquals(o2Id, nodes.get(0).getId()); + assertEquals(ASSOC_TYPE_CM_REFERENCES, nodes.get(0).getAssociation().getAssocType()); + + params = new HashMap<>(); + params.put("where", "(assocType='"+ASSOC_TYPE_CM_PARTS+"')"); + + response = getAll(getNodeTargetsUrl(o2Id), user1, paging, params, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(1, nodes.size()); + assertEquals(o1Id, nodes.get(0).getId()); + assertEquals(ASSOC_TYPE_CM_PARTS, nodes.get(0).getAssociation().getAssocType()); + + response = getAll(getNodeSourcesUrl(o2Id), user1, paging, params, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(1, nodes.size()); + assertEquals(o1Id, nodes.get(0).getId()); + assertEquals(ASSOC_TYPE_CM_PARTS, nodes.get(0).getAssociation().getAssocType()); + + + // -ve test - unauthenticated - belts-and-braces ;-) + delete(getNodeTargetsUrl(o1Id), null, o2Id, 401); + + // remove assocs - specific type - in one direction + params = new HashMap<>(2); + params.put(PARAM_ASSOC_TYPE, ASSOC_TYPE_CM_REFERENCES); + delete(getNodeTargetsUrl(o1Id), user1, o2Id, params, 204); + + response = getAll(getNodeTargetsUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(1, nodes.size()); + + response = getAll(getNodeSourcesUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + response = getAll(getNodeTargetsUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + response = getAll(getNodeSourcesUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(1, nodes.size()); + + params = new HashMap<>(2); + params.put(PARAM_ASSOC_TYPE, ASSOC_TYPE_CM_PARTS); + delete(getNodeTargetsUrl(o1Id), user1, o2Id, params, 204); + + response = getAll(getNodeTargetsUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeSourcesUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + response = getAll(getNodeTargetsUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(2, nodes.size()); + + response = getAll(getNodeSourcesUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + // remove assocs - both types at once (ie. no assocType param) - in the other direction + delete(getNodeTargetsUrl(o2Id), user1, o1Id, 204); + + // empty lists - after + + response = getAll(getNodeTargetsUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeSourcesUrl(o1Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeTargetsUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + response = getAll(getNodeSourcesUrl(o2Id), user1, paging, null, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(0, nodes.size()); + + + // -ve test - model integrity + tgt = new AssocTarget(f2Id, ASSOC_TYPE_CM_REFERENCES); + post(getNodeTargetsUrl(o1Id), user1, toJsonAsStringNonNull(tgt), 422); + + // -ve test - duplicate assoc + tgt = new AssocTarget(o1Id, ASSOC_TYPE_CM_REFERENCES); + post(getNodeTargetsUrl(o2Id), user1, toJsonAsStringNonNull(tgt), 201); + post(getNodeTargetsUrl(o2Id), user1, toJsonAsStringNonNull(tgt), 409); + } + finally + { + // some cleanup + Map params = Collections.singletonMap("permanent", "true"); + delete(URL_NODES, user1, f1Id, params, 204); + delete(URL_NODES, user1, f2Id, params, 204); + } + } + + @Override + public String getScope() + { + return "public"; + } +} 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 281b8fe5bd..0a39fff13c 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 @@ -25,6 +25,8 @@ */ package org.alfresco.rest.api.tests.client.data; +import org.alfresco.rest.api.model.Assoc; + import java.util.Date; import java.util.List; import java.util.Map; @@ -256,6 +258,18 @@ public class Node this.relativePath = relativePath; } + protected Assoc association; + + public Assoc getAssociation() + { + return association; + } + + public void setAssociation(Assoc association) + { + this.association = association; + } + public void expected(Object o) { Node other = (Node) o;