From 395d7ded57931ebafbef1ed920cbb7532b789ad9 Mon Sep 17 00:00:00 2001 From: Krystian Dabrowski <98942253+krdabrowski@users.noreply.github.com> Date: Fri, 6 Oct 2023 15:36:46 +0200 Subject: [PATCH] ACS-5471: Secondary path support (#2213) * ACS-5471: Secondary path support * ACS-5471: Secondary path support * ACS-5471: Secondary path support * ACS-5471: Secondary path support * ACS-5471: Secondary path support * ACS-5471: Secondary path support * ACS-5471: Secondary path support * ACS-5471: Secondary path support - fixed test method name * ACS-5471: Secondary path support - trying to fix failing on CI DispositionScheduleLinkedRecordsTest * ACS-5471: Secondary path support - trying to fix failing on CI DispositionScheduleLinkedRecordsTest * ACS-5471: Secondary path support - trying to fix failing CI due to DispositionScheduleLinkedRecordsTest * ACS-5471: Test adjustment to follow same behavior as introduced by ACS-5325 * ACS-5471: Fixing docker issues --------- Co-authored-by: mpichura --- packaging/docker-alfresco/Dockerfile | 2 +- .../java/org/alfresco/rest/requests/Node.java | 115 ++++++- .../rest/nodes/NodesParentChildrenTests.java | 7 +- pom.xml | 2 +- .../alfresco/repo/event2/EventGenerator.java | 24 ++ .../repo/event2/NodeEventConsolidator.java | 47 ++- .../repo/event2/NodeResourceHelper.java | 49 ++- .../resources/alfresco/repository.properties | 1 + .../event2/ChildAssociationRepoEventIT.java | 290 ++++++++++-------- .../event2/EventConsolidatorUnitTest.java | 116 +++++-- .../event2/NodeResourceHelperUnitTest.java | 64 ++++ scripts/ci/docker-compose/docker-compose.yaml | 2 +- 12 files changed, 528 insertions(+), 191 deletions(-) diff --git a/packaging/docker-alfresco/Dockerfile b/packaging/docker-alfresco/Dockerfile index 9e2f1f3962..ad6e7e8e2f 100644 --- a/packaging/docker-alfresco/Dockerfile +++ b/packaging/docker-alfresco/Dockerfile @@ -98,4 +98,4 @@ EXPOSE 10001 # For remote debug EXPOSE 8000 -USER ${IMAGEUSERNAME} \ No newline at end of file +USER ${IMAGEUSERNAME} diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Node.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Node.java index e41da3a530..c45164d8b3 100644 --- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Node.java +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Node.java @@ -35,6 +35,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.List; +import java.util.stream.Stream; import io.restassured.http.ContentType; import org.alfresco.rest.core.JsonBodyGenerator; @@ -51,9 +52,11 @@ import org.alfresco.rest.model.RestCommentModelsCollection; import org.alfresco.rest.model.RestNodeAssocTargetModel; import org.alfresco.rest.model.RestNodeAssociationModel; import org.alfresco.rest.model.RestNodeAssociationModelCollection; +import org.alfresco.rest.model.RestNodeAssociationTypeModel; import org.alfresco.rest.model.RestNodeBodyModel; import org.alfresco.rest.model.RestNodeBodyMoveCopyModel; import org.alfresco.rest.model.RestNodeChildAssocModelCollection; +import org.alfresco.rest.model.RestNodeChildAssociationModel; import org.alfresco.rest.model.RestNodeModel; import org.alfresco.rest.model.RestNodeModelsCollection; import org.alfresco.rest.model.RestRatingModel; @@ -72,6 +75,7 @@ import org.alfresco.rest.model.body.RestNodeLockBodyModel; import org.alfresco.rest.model.builder.NodesBuilder; import org.alfresco.utility.Utility; import org.alfresco.utility.model.RepoTestModel; +import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.testng.reporters.Files; @@ -824,25 +828,118 @@ public class Node extends ModelRequest } /** - * Create secondary children association using POST call 'nodes/{nodeId}/secondary-children - * Use a list of secondary children nodes + * Creates a secondary child association using POST call to: 'nodes/{nodeId}/secondary-children'. * - * @return a collection of nodes + * @param secondaryChild - node, which should become a secondary child + * @return a node's parent-child association */ - public RestNodeChildAssocModelCollection createSecondaryChildren(String secondaryChildren) + public RestNodeChildAssociationModel addSecondaryChild(RepoTestModel secondaryChild) { - RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, secondaryChildren, "nodes/{nodeId}/secondary-children?{parameters}", repoModel.getNodeRef(), restWrapper.getParameters()); + return addSecondaryChild("cm:contains", secondaryChild); + } + + /** + * Creates a secondary child association using POST call to: 'nodes/{nodeId}/secondary-children'. + * + * @param associationType - type of secondary parent-child relationship association + * @param secondaryChild - node, which should become a secondary child + * @return a node's parent-child association + */ + public RestNodeChildAssociationModel addSecondaryChild(String associationType, RepoTestModel secondaryChild) + { + return addSecondaryChild(new RestNodeChildAssociationModel(secondaryChild.getNodeRef(), associationType)); + } + + /** + * Creates a secondary child association using POST call to: 'nodes/{nodeId}/secondary-children'. + * + * @param secondaryChildAssociation - node's secondary parent-child association model + * @return a node's parent-child association + */ + public RestNodeChildAssociationModel addSecondaryChild(RestNodeChildAssociationModel secondaryChildAssociation) + { + RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, secondaryChildAssociation.toJson(), "nodes/{nodeId}/secondary-children?{parameters}", repoModel.getNodeRef(), restWrapper.getParameters()); + return restWrapper.processModel(RestNodeChildAssociationModel.class, request); + } + + /** + * Creates a secondary children association using POST call to: 'nodes/{nodeId}/secondary-children'. + * + * @param secondaryChildren - nodes, which should become secondary children + * @return a collection of node's parent-child associations + */ + public RestNodeChildAssocModelCollection addSecondaryChildren(RepoTestModel... secondaryChildren) + { + return addSecondaryChildren("cm:contains", secondaryChildren); + } + + /** + * Creates a secondary children association using POST call to: 'nodes/{nodeId}/secondary-children'. + * + * @param associationType - type of secondary parent-child relationship association + * @param secondaryChildren - nodes, which should become secondary children + * @return a collection of node's parent-child associations + */ + public RestNodeChildAssocModelCollection addSecondaryChildren(String associationType, RepoTestModel... secondaryChildren) + { + return addSecondaryChildren(Stream.of(secondaryChildren) + .map(child -> new RestNodeChildAssociationModel(child.getNodeRef(), associationType)) + .toArray(RestNodeChildAssociationModel[]::new)); + } + + /** + * Creates a secondary children association using POST call to: 'nodes/{nodeId}/secondary-children'. + * + * @param secondaryChildrenAssociations - node's secondary parent-child association models + * @return a collection of node's parent-child associations + */ + public RestNodeChildAssocModelCollection addSecondaryChildren(RestNodeChildAssociationModel... secondaryChildrenAssociations) + { + String requestBody = arrayToJson(Stream.of(secondaryChildrenAssociations).toList()); + RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, requestBody, "nodes/{nodeId}/secondary-children?{parameters}", repoModel.getNodeRef(), restWrapper.getParameters()); return restWrapper.processModels(RestNodeChildAssocModelCollection.class, request); } /** - * Delete secondary children using DELETE call 'nodes/{nodeId}/secondary-children/{childId} + * Removes secondary child association using DELETE call 'nodes/{nodeId}/secondary-children/{childId}'. * - * @return a collection of nodes + * @param secondaryChild - node, which should NOT be a secondary child anymore */ - public void deleteSecondaryChild(RestNodeAssociationModel child) + public void removeSecondaryChild(RepoTestModel secondaryChild) { - RestRequest request = RestRequest.simpleRequest(HttpMethod.DELETE, "nodes/{nodeId}/secondary-children/{childId}?{parameters}", repoModel.getNodeRef(), child.getId(), restWrapper.getParameters()); + removeSecondaryChild(null, secondaryChild); + } + + /** + * Removes secondary child association using DELETE call 'nodes/{nodeId}/secondary-children/{childId}'. + * + * @param associationType - type of secondary parent-child relationship association + * @param secondaryChild - node, which should NOT be a secondary child anymore + */ + public void removeSecondaryChild(String associationType, RepoTestModel secondaryChild) + { + RestNodeAssociationModel associationModel = new RestNodeAssociationModel(); + RestNodeAssociationTypeModel associationTypeModel = new RestNodeAssociationTypeModel(); + if (associationType != null) + { + associationTypeModel.setAssocType(associationType); + } + associationModel.setAssociation(associationTypeModel); + associationModel.setId(secondaryChild.getNodeRef()); + removeSecondaryChild(associationModel); + } + + /** + * Removes secondary child association using DELETE call 'nodes/{nodeId}/secondary-children/{childId}'. + * + * @param secondaryChildAssociation - node's secondary parent-child association to remove + */ + public void removeSecondaryChild(RestNodeAssociationModel secondaryChildAssociation) + { + String parameters = StringUtils.isNotEmpty(secondaryChildAssociation.getAssociation().getAssocType()) ? + "assocType=" + secondaryChildAssociation.getAssociation().getAssocType() + "&" + restWrapper.getParameters() : + restWrapper.getParameters(); + RestRequest request = RestRequest.simpleRequest(HttpMethod.DELETE, "nodes/{nodeId}/secondary-children/{childId}?{parameters}", repoModel.getNodeRef(), secondaryChildAssociation.getId(), parameters); restWrapper.processEmptyModel(request); } diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/nodes/NodesParentChildrenTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/nodes/NodesParentChildrenTests.java index 417eb99d19..a6ba80ded1 100644 --- a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/nodes/NodesParentChildrenTests.java +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/nodes/NodesParentChildrenTests.java @@ -125,11 +125,10 @@ public class NodesParentChildrenTests extends RestTest RestNodeChildAssociationModel childAssoc1 = new RestNodeChildAssociationModel(nodesBuilder.getNode("f1").getId(), "cm:contains"); RestNodeChildAssociationModel childAssoc2 = new RestNodeChildAssociationModel(nodesBuilder.getNode("f2").getId(), "cm:contains"); RestNodeChildAssociationModel childAssoc3 = new RestNodeChildAssociationModel(nodesBuilder.getNode("f3").getId(), "cm:preferenceImage"); - String secondaryChildrenBody = "[" + childAssoc1.toJson() + "," + childAssoc2.toJson() + "," + childAssoc3.toJson() + "]"; STEP("3. Create secondary child associations using POST /nodes/{nodeId}/secondary-children"); RestNodeChildAssocModelCollection secondaryChildAssoc = restClient.withCoreAPI().usingNode(nodesBuilder.getNode("F1").toContentModel()) - .createSecondaryChildren(secondaryChildrenBody); + .addSecondaryChildren(childAssoc1, childAssoc2, childAssoc3); restClient.assertStatusCodeIs(HttpStatus.CREATED); secondaryChildAssoc.getEntryByIndex(0).assertThat().field("childId").is(childAssoc1.getChildId()); secondaryChildAssoc.getEntryByIndex(1).assertThat().field("childId").is(childAssoc2.getChildId()); @@ -142,7 +141,7 @@ public class NodesParentChildrenTests extends RestTest secondaryChildren.assertThat().entriesListCountIs(2); STEP("5. Check using DELETE /nodes/{nodeId}/secondary-children/{childId} that a secondary child can be deleted"); - restClient.withCoreAPI().usingNode(nodesBuilder.getNode("F1").toContentModel()).deleteSecondaryChild(secondaryChildren.getEntryByIndex(0)); + restClient.withCoreAPI().usingNode(nodesBuilder.getNode("F1").toContentModel()).removeSecondaryChild(secondaryChildren.getEntryByIndex(0)); restClient.assertStatusCodeIs(HttpStatus.NO_CONTENT); STEP("6. Check using GET /nodes/{nodeId}/secondary-children that a secondary child association was deleted"); @@ -182,7 +181,7 @@ public class NodesParentChildrenTests extends RestTest STEP("2. Create secondary child associations using POST /nodes/{nodeId}/secondary-children"); RestNodeChildAssociationModel childAssoc = new RestNodeChildAssociationModel(nodesBuilder.getNode("f1").getId(), "cm:contains"); - restClient.withCoreAPI().usingNode(nodesBuilder.getNode("F1").toContentModel()).createSecondaryChildren(childAssoc.toJson()); + restClient.withCoreAPI().usingNode(nodesBuilder.getNode("F1").toContentModel()).addSecondaryChild(childAssoc); restClient.assertStatusCodeIs(HttpStatus.CREATED); STEP("3. Get all parents for file 'f1' - both primary and secondary"); diff --git a/pom.xml b/pom.xml index 0600eafb86..aa92784105 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ 5.0.0-A3 4.0.0-A3 7.0 - 0.0.23 + 0.0.24 1.9.20.1 6.0.12 diff --git a/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java b/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java index 6e59a5f498..a6dc0546c4 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java +++ b/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java @@ -311,12 +311,24 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin public void onCreateChildAssociation(ChildAssociationRef childAssociationRef, boolean isNewNode) { getEventConsolidator(childAssociationRef).onCreateChildAssociation(childAssociationRef, isNewNode); + if (!childAssociationRef.isPrimary()) + { + // if this is a secondary relationship simulate node move event to store state of previous secondary parents + ChildAssociationRef oldChildAssociationRef = childAssociationWithoutParentOf(childAssociationRef); + getEventConsolidator(childAssociationRef.getChildRef()).onMoveNode(oldChildAssociationRef, childAssociationRef); + } } @Override public void beforeDeleteChildAssociation(ChildAssociationRef childAssociationRef) { getEventConsolidator(childAssociationRef).beforeDeleteChildAssociation(childAssociationRef); + if (!childAssociationRef.isPrimary()) + { + // if this is a secondary relationship simulate node move event to store state of previous secondary parents + ChildAssociationRef newChildAssociationRef = childAssociationWithoutParentOf(childAssociationRef); + getEventConsolidator(childAssociationRef.getChildRef()).onMoveNode(childAssociationRef, newChildAssociationRef); + } } @Override @@ -503,6 +515,18 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin return ZonedDateTime.ofInstant(commitTimeMs, ZoneOffset.UTC); } + private static ChildAssociationRef childAssociationWithoutParentOf(ChildAssociationRef childAssociationRef) + { + return new ChildAssociationRef( + null, + null, + childAssociationRef.getQName(), + childAssociationRef.getChildRef(), + childAssociationRef.isPrimary(), + childAssociationRef.getNthSibling() + ); + } + @Override protected void onBootstrap(ApplicationEvent applicationEvent) { diff --git a/repository/src/main/java/org/alfresco/repo/event2/NodeEventConsolidator.java b/repository/src/main/java/org/alfresco/repo/event2/NodeEventConsolidator.java index ea38b64e26..b6db486c16 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/NodeEventConsolidator.java +++ b/repository/src/main/java/org/alfresco/repo/event2/NodeEventConsolidator.java @@ -65,6 +65,7 @@ public class NodeEventConsolidator extends EventConsolidator primaryHierarchyBefore; + private List secondaryParentsBefore; private boolean resourceBeforeAllFieldsNull = true; public NodeEventConsolidator(NodeResourceHelper nodeResourceHelper) @@ -144,7 +145,25 @@ public class NodeEventConsolidator extends EventConsolidator secondaryParents = helper.getSecondaryParents(newChildAssocRef.getChildRef()); + if (newChildAssocRef.getParentRef() != null) + { + // on create secondary child association event takes place - recreate secondary parents previous state + secondaryParents.remove(newChildAssocRef.getParentRef().getId()); + } + else if(oldChildAssocRef.getParentRef() != null && !secondaryParents.contains(oldChildAssocRef.getParentRef().getId())) + { + // before remove secondary child association event takes place - recreate secondary parents previous state + secondaryParents.add(oldChildAssocRef.getParentRef().getId()); + } + setSecondaryParentsBefore(secondaryParents); + } } @Override @@ -174,7 +193,7 @@ public class NodeEventConsolidator extends EventConsolidator secondaryParents) + { + if (this.secondaryParentsBefore == null) + { + this.secondaryParentsBefore = secondaryParents; + } + } + + List getSecondaryParentsBefore() + { + return secondaryParentsBefore; + } + private NodeResource buildNodeResource() { if (resourceBuilder == null) @@ -283,7 +315,7 @@ public class NodeEventConsolidator extends EventConsolidator> localizedProps =helper.getLocalizedPropertiesBefore(changedPropsBefore, after); + Map> localizedProps = helper.getLocalizedPropertiesBefore(changedPropsBefore, after); if (!localizedProps.isEmpty()) { builder.setLocalizedProperties(localizedProps); @@ -309,8 +341,7 @@ public class NodeEventConsolidator extends EventConsolidator mapUserCache = new HashMap<>(2); - return NodeResource.builder().setId(nodeRef.getId()) - .setName((String) properties.get(ContentModel.PROP_NAME)) - .setNodeType(getQNamePrefixString(type)) - .setIsFile(isSubClass(type, ContentModel.TYPE_CONTENT)) - .setIsFolder(isSubClass(type, ContentModel.TYPE_FOLDER)) - .setCreatedByUser(getUserInfo((String) properties.get(ContentModel.PROP_CREATOR), mapUserCache)) - .setCreatedAt(getZonedDateTime((Date)properties.get(ContentModel.PROP_CREATED))) - .setModifiedByUser(getUserInfo((String) properties.get(ContentModel.PROP_MODIFIER), mapUserCache)) - .setModifiedAt(getZonedDateTime((Date)properties.get(ContentModel.PROP_MODIFIED))) - .setContent(getContentInfo(properties)) - .setPrimaryAssocQName(getPrimaryAssocQName(nodeRef)) - .setPrimaryHierarchy(PathUtil.getNodeIdsInReverse(path, false)) - .setProperties(mapToNodeProperties(properties)) - .setLocalizedProperties(mapToNodeLocalizedProperties(properties)) - .setAspectNames(getMappedAspects(nodeRef)); + return NodeResource.builder() + .setId(nodeRef.getId()) + .setName((String) properties.get(ContentModel.PROP_NAME)) + .setNodeType(getQNamePrefixString(type)) + .setIsFile(isSubClass(type, ContentModel.TYPE_CONTENT)) + .setIsFolder(isSubClass(type, ContentModel.TYPE_FOLDER)) + .setCreatedByUser(getUserInfo((String) properties.get(ContentModel.PROP_CREATOR), mapUserCache)) + .setCreatedAt(getZonedDateTime((Date)properties.get(ContentModel.PROP_CREATED))) + .setModifiedByUser(getUserInfo((String) properties.get(ContentModel.PROP_MODIFIER), mapUserCache)) + .setModifiedAt(getZonedDateTime((Date)properties.get(ContentModel.PROP_MODIFIED))) + .setContent(getContentInfo(properties)) + .setPrimaryAssocQName(getPrimaryAssocQName(nodeRef)) + .setPrimaryHierarchy(PathUtil.getNodeIdsInReverse(path, false)) + .setProperties(mapToNodeProperties(properties)) + .setLocalizedProperties(mapToNodeLocalizedProperties(properties)) + .setAspectNames(getMappedAspects(nodeRef)) + .setSecondaryParents(getSecondaryParents(nodeRef)); } private boolean isSubClass(QName className, QName ofClassQName) @@ -413,6 +417,21 @@ public class NodeResourceHelper implements InitializingBean return PathUtil.getNodeIdsInReverse(path, showLeaf); } + /** + * Gathers node's secondary parents. + * + * @param nodeRef - node reference + * @return a list of node's secondary parents. + */ + public List getSecondaryParents(final NodeRef nodeRef) + { + return nodeService.getParentAssocs(nodeRef).stream() + .filter(not(ChildAssociationRef::isPrimary)) + .map(ChildAssociationRef::getParentRef) + .map(NodeRef::getId) + .collect(Collectors.toList()); + } + public PermissionService getPermissionService() { return permissionService; diff --git a/repository/src/main/resources/alfresco/repository.properties b/repository/src/main/resources/alfresco/repository.properties index 96fa268f7b..94eae2dbec 100644 --- a/repository/src/main/resources/alfresco/repository.properties +++ b/repository/src/main/resources/alfresco/repository.properties @@ -1231,6 +1231,7 @@ repo.event2.filter.users= repo.event2.topic.endpoint=amqp:topic:alfresco.repo.event2 # Specifies if messages should be enqueued in in-memory queue or sent directly to the topic repo.event2.queue.skip=false +#repo.event2.topic.endpoint=amqp:topic:VirtualTopic.alfresco.repo.event2 # Thread pool for async enqueue of repo events repo.event2.queue.enqueueThreadPool.priority=1 repo.event2.queue.enqueueThreadPool.coreSize=8 diff --git a/repository/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java index 486d0041f9..aa46a9e733 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java +++ b/repository/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java @@ -28,6 +28,7 @@ package org.alfresco.repo.event2; import java.util.Arrays; import java.util.List; +import java.util.stream.IntStream; import org.alfresco.model.ContentModel; import org.alfresco.repo.event.v1.model.ChildAssociationResource; @@ -64,12 +65,11 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - retryingTransactionHelper.doInTransaction(() -> - nodeService.addChild( - parentNodeRef, - childNodeRef, - ContentModel.ASSOC_CONTAINS, - QName.createQName(TEST_NAMESPACE, assocLocalName))); + retryingTransactionHelper.doInTransaction(() -> nodeService.addChild( + parentNodeRef, + childNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(TEST_NAMESPACE, assocLocalName))); List childAssociationRefs = retryingTransactionHelper.doInTransaction(() -> nodeService.getChildAssocs(parentNodeRef)); @@ -79,8 +79,30 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - final RepoEvent> childAssocRepoEvent = getFilteredEvent(EventType.CHILD_ASSOC_CREATED, 0); + // node event + final RepoEvent> nodeRepoEvent = getRepoEventWithoutWait(3); + assertEquals("Wrong repo event type.", EventType.NODE_UPDATED.getType(), nodeRepoEvent.getType()); + assertNotNull("Repo event ID is not available.", nodeRepoEvent.getId()); + assertNotNull("Source is not available", nodeRepoEvent.getSource()); + assertEquals("Repo event source is not available.", + "/" + descriptorService.getCurrentRepositoryDescriptor().getId(), + nodeRepoEvent.getSource().toString()); + assertNotNull("Repo event creation time is not available.", nodeRepoEvent.getTime()); + assertEquals("Invalid repo event datacontenttype", "application/json", + nodeRepoEvent.getDatacontenttype()); + assertNotNull(nodeRepoEvent.getDataschema()); + assertEquals(EventJSONSchema.NODE_UPDATED_V1.getSchema(), nodeRepoEvent.getDataschema()); + final EventData nodeResourceEventData = getEventData(nodeRepoEvent); + assertNotNull("Event data group ID is not available. ", nodeResourceEventData.getEventGroupId()); + assertNotNull("resourceBefore property is not available", nodeResourceEventData.getResourceBefore()); + + final NodeResource nodeResource = getNodeResource(nodeRepoEvent); + final NodeResource nodeResourceBefore = getNodeResourceBefore(nodeRepoEvent); + assertNotSame("Secondary parents actual and earlier state should differ", nodeResource.getSecondaryParents(), nodeResourceBefore.getSecondaryParents()); + + // child association event + final RepoEvent> childAssocRepoEvent = getFilteredEvent(EventType.CHILD_ASSOC_CREATED, 0); assertEquals("Wrong repo event type.", EventType.CHILD_ASSOC_CREATED.getType(), childAssocRepoEvent.getType()); assertNotNull("Repo event ID is not available.", childAssocRepoEvent.getId()); assertNotNull("Source is not available", childAssocRepoEvent.getSource()); @@ -93,16 +115,18 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent assertNotNull(childAssocRepoEvent.getDataschema()); assertEquals(EventJSONSchema.CHILD_ASSOC_CREATED_V1.getSchema(), childAssocRepoEvent.getDataschema()); - final EventData nodeResourceEventData = getEventData(childAssocRepoEvent); - // EventData attributes - assertNotNull("Event data group ID is not available. ", nodeResourceEventData.getEventGroupId()); - assertNull("resourceBefore property is not available", nodeResourceEventData.getResourceBefore()); + final EventData childAssocResourceEventData = getEventData(childAssocRepoEvent); + assertNotNull("Event data group ID is not available. ", childAssocResourceEventData.getEventGroupId()); + assertNull("resourceBefore property is not available", childAssocResourceEventData.getResourceBefore()); final ChildAssociationResource childAssociationResource = getChildAssocResource(childAssocRepoEvent); assertEquals("Wrong parent", parentNodeRef.getId(), childAssociationResource.getParent().getId()); assertEquals("Wrong child", childNodeRef.getId(), childAssociationResource.getChild().getId()); assertEquals("Wrong assoc type", "cm:contains", childAssociationResource.getAssocType()); assertEquals("Wrong assoc name", "ce:" + assocLocalName, childAssociationResource.getAssocQName()); + + assertEquals("Node and child association events should have same eventGroupId", nodeResourceEventData.getEventGroupId(), childAssocResourceEventData.getEventGroupId()); + assertTrue("Wrong node's secondary parents", nodeResource.getSecondaryParents().contains(childAssociationResource.getParent().getId())); } @Test @@ -143,8 +167,30 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(6); - final RepoEvent> childAssocRepoEvent = getFilteredEvent(EventType.CHILD_ASSOC_DELETED, 0); + // node repo event + final RepoEvent> nodeRepoEvent = getRepoEventWithoutWait(5); + assertEquals("Wrong repo event type.", EventType.NODE_UPDATED.getType(), nodeRepoEvent.getType()); + assertNotNull("Repo event ID is not available.", nodeRepoEvent.getId()); + assertNotNull("Source is not available", nodeRepoEvent.getSource()); + assertEquals("Repo event source is not available.", + "/" + descriptorService.getCurrentRepositoryDescriptor().getId(), + nodeRepoEvent.getSource().toString()); + assertNotNull("Repo event creation time is not available.", nodeRepoEvent.getTime()); + assertEquals("Invalid repo event datacontenttype", "application/json", + nodeRepoEvent.getDatacontenttype()); + assertNotNull(nodeRepoEvent.getDataschema()); + assertEquals(EventJSONSchema.NODE_UPDATED_V1.getSchema(), nodeRepoEvent.getDataschema()); + final EventData nodeResourceEventData = getEventData(nodeRepoEvent); + assertNotNull("Event data group ID is not available. ", nodeResourceEventData.getEventGroupId()); + assertNotNull("resourceBefore property is not available", nodeResourceEventData.getResourceBefore()); + + final NodeResource nodeResource = getNodeResource(nodeRepoEvent); + final NodeResource nodeResourceBefore = getNodeResourceBefore(nodeRepoEvent); + assertNotSame("Secondary parents actual and earlier state should differ", nodeResource.getSecondaryParents(), nodeResourceBefore.getSecondaryParents()); + + // child association repo event + final RepoEvent> childAssocRepoEvent = getFilteredEvent(EventType.CHILD_ASSOC_DELETED, 0); assertEquals("Wrong repo event type.", EventType.CHILD_ASSOC_DELETED.getType(), childAssocRepoEvent.getType()); assertNotNull("Repo event ID is not available. ", childAssocRepoEvent.getId()); assertNotNull("Source is not available", childAssocRepoEvent.getSource()); @@ -156,15 +202,17 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent assertNotNull(childAssocRepoEvent.getDataschema()); assertEquals(EventJSONSchema.CHILD_ASSOC_DELETED_V1.getSchema(), childAssocRepoEvent.getDataschema()); - final EventData nodeResourceEventData = getEventData(childAssocRepoEvent); - // EventData attributes - assertNotNull("Event data group ID is not available. ", nodeResourceEventData.getEventGroupId()); - assertNull("resourceBefore property is not available", nodeResourceEventData.getResourceBefore()); + final EventData childAssocResourceEventData = getEventData(childAssocRepoEvent); + assertNotNull("Event data group ID is not available. ", childAssocResourceEventData.getEventGroupId()); + assertNull("resourceBefore property is not available", childAssocResourceEventData.getResourceBefore()); final ChildAssociationResource childAssociationResource = getChildAssocResource(childAssocRepoEvent); assertEquals("Wrong parent", parentNodeRef.getId(), childAssociationResource.getParent().getId()); assertEquals("Wrong child", childNodeRef.getId(), childAssociationResource.getChild().getId()); assertEquals("Wrong assoc type", "cm:contains", childAssociationResource.getAssocType()); + + assertEquals("Node and child association events should have same eventGroupId", nodeResourceEventData.getEventGroupId(), childAssocResourceEventData.getEventGroupId()); + assertTrue("Wrong node's secondary parents", nodeResourceBefore.getSecondaryParents().contains(childAssociationResource.getParent().getId())); } @Test @@ -179,17 +227,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(3); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(4); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); retryingTransactionHelper.doInTransaction(() -> nodeService.addChild( @@ -213,11 +254,14 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent }); checkNumOfEvents(8); - - // 3 assoc.child.Created events should be created + // 1 node.Updated events should be created + List>> nodeUpdateEvent = getFilteredEvents(EventType.NODE_UPDATED); + assertEquals("Wrong association events number", 1, nodeUpdateEvent.size()); + + // 3 assoc.child.Created events should be created List>> childAssocEvents = getFilteredEvents(EventType.CHILD_ASSOC_CREATED); - assertEquals("Wrong association events number",3, childAssocEvents.size()); + assertEquals("Wrong association events number", 3, childAssocEvents.size()); } @Test @@ -231,17 +275,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent List parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef); checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); retryingTransactionHelper.doInTransaction(() -> { for (NodeRef parent : parents) @@ -269,9 +306,13 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent }); checkNumOfEvents(8); + + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(5); + assertEquals("Wrong repo event type.", EventType.NODE_UPDATED.getType(), resultRepoEvent.getType()); + // 3 assoc.child.Created events should be created List>> childAssocEvents = getFilteredEvents(EventType.CHILD_ASSOC_CREATED); - assertEquals("Wrong association events number",3, childAssocEvents.size()); + assertEquals("Wrong association events number", 3, childAssocEvents.size()); // All events in the transaction should have the same eventGroupId String assocEventGroupID1 = getEventData(childAssocEvents.get(0)).getEventGroupId(); @@ -294,17 +335,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(3); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(4); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); for (NodeRef parent : parents) { @@ -331,9 +365,14 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent }); checkNumOfEvents(10); + + // 3 node.Updated events should be created + List>> nodeUpdateEvents = getFilteredEvents(EventType.NODE_UPDATED); + assertEquals("Wrong node update events number", 3, nodeUpdateEvents.size()); + // 3 assoc.child.Created events should be created List>> childAssocEvents = getFilteredEvents(EventType.CHILD_ASSOC_CREATED); - assertEquals("Wrong association events number",3, childAssocEvents.size()); + assertEquals("Wrong association events number", 3, childAssocEvents.size()); assertEquals(parent1NodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getParent().getId()); assertEquals(childNodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getChild().getId()); @@ -360,17 +399,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(3); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(4); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); retryingTransactionHelper.doInTransaction(() -> { for (NodeRef child : children) @@ -389,9 +421,14 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent }); checkNumOfEvents(10); + + // 3 node.Updated events should be created + List>> nodeUpdateEvents = getFilteredEvents(EventType.NODE_UPDATED); + assertEquals("Wrong node update events number", 3, nodeUpdateEvents.size()); + // 3 assoc.child.Created events should be created List>> childAssocEvents = getFilteredEvents(EventType.CHILD_ASSOC_CREATED); - assertEquals("Wrong association events number",3, childAssocEvents.size()); + assertEquals("Wrong association events number", 3, childAssocEvents.size()); } @Test @@ -406,17 +443,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(3); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(4); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); for (NodeRef child : children) { @@ -433,9 +463,14 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent }); checkNumOfEvents(10); + + // 3 node.Updated events should be created + List>> nodeUpdateEvents = getFilteredEvents(EventType.NODE_UPDATED); + assertEquals("Wrong node update events number", 3, nodeUpdateEvents.size()); + // 3 assoc.child.Created events should be created List>> childAssocEvents = getFilteredEvents(EventType.CHILD_ASSOC_CREATED); - assertEquals("Wrong association events number",3, childAssocEvents.size()); + assertEquals("Wrong association events number", 3, childAssocEvents.size()); assertEquals(parentNodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getParent().getId()); assertEquals(child1NodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getChild().getId()); @@ -462,17 +497,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(3); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(4); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); retryingTransactionHelper.doInTransaction(() -> nodeService.addChild(parents, childNodeRef, ContentModel.ASSOC_CONTAINS, @@ -503,13 +531,17 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(12); + // 2 node.Updated events should be created + List>> nodeUpdateEvents = getFilteredEvents(EventType.NODE_UPDATED); + assertEquals("Wrong node update events number", 2, nodeUpdateEvents.size()); + // 3 assoc.child.Deleted events should be created List>> childAssocEvents = getFilteredEvents(EventType.CHILD_ASSOC_DELETED); - assertEquals("Wrong association events number",3, childAssocEvents.size()); + assertEquals("Wrong association events number", 3, childAssocEvents.size()); } @Test - public void testDeleteAssociationOneParentMultipleChildrenDifferentTransactions() + public void testDeleteAssociationMultipleParentOneChildrenDifferentTransactions() { final NodeRef parent1NodeRef = createNode(ContentModel.TYPE_FOLDER); final NodeRef parent2NodeRef = createNode(ContentModel.TYPE_FOLDER); @@ -520,17 +552,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(3); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(4); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); retryingTransactionHelper.doInTransaction(() -> nodeService.addChild(parents, childNodeRef, ContentModel.ASSOC_CONTAINS, @@ -588,17 +613,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(3); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(4); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); retryingTransactionHelper.doInTransaction(() -> { for (NodeRef child : children) @@ -621,9 +639,13 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(17); + // 6 node.Updated events should be created + List>> nodeUpdateEvents = getFilteredEvents(EventType.NODE_UPDATED); + assertEquals("Wrong node update events number", 6, nodeUpdateEvents.size()); + // 3 assoc.child.Deleted events should be created List>> childAssocEvents = getFilteredEvents(EventType.CHILD_ASSOC_DELETED); - assertEquals("Wrong association events number",3, childAssocEvents.size()); + assertEquals("Wrong association events number", 3, childAssocEvents.size()); } @Test @@ -638,17 +660,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(4); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(3); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(4); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2, 3, 4).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); retryingTransactionHelper.doInTransaction(() -> nodeService.addChild(parents, childNodeRef, ContentModel.ASSOC_CONTAINS, @@ -672,9 +687,13 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(12); + // 2 node.Updated events should be created + List>> nodeUpdateEvents = getFilteredEvents(EventType.NODE_UPDATED); + assertEquals("Wrong node update events number", 2, nodeUpdateEvents.size()); + // 3 assoc.child.Deleted events should be created List>> childAssocEvents = getFilteredEvents(EventType.CHILD_ASSOC_DELETED); - assertEquals("Wrong association events number",3, childAssocEvents.size()); + assertEquals("Wrong association events number", 3, childAssocEvents.size()); } @Test @@ -685,11 +704,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent checkNumOfEvents(2); - RepoEvent> resultRepoEvent = getRepoEventWithoutWait(1); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); - - resultRepoEvent = getRepoEventWithoutWait(2); - assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + IntStream.of(1, 2).forEach(i -> { + RepoEvent> resultRepoEvent = getRepoEventWithoutWait(i); + assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType()); + }); retryingTransactionHelper.doInTransaction(() -> { @@ -712,10 +730,10 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent // Check the node events occur before the child association event List> repoEvents = getRepoEventsContainer().getEvents(); - assertEquals("org.alfresco.event.node.Created", repoEvents.get(0).getType()); - assertEquals("org.alfresco.event.node.Created", repoEvents.get(1).getType()); - assertEquals("org.alfresco.event.node.Updated", repoEvents.get(2).getType()); - assertEquals("org.alfresco.event.node.Updated", repoEvents.get(3).getType()); - assertEquals("org.alfresco.event.assoc.child.Created", repoEvents.get(4).getType()); + assertEquals(EventType.NODE_CREATED.getType(), repoEvents.get(0).getType()); + assertEquals(EventType.NODE_CREATED.getType(), repoEvents.get(1).getType()); + assertEquals(EventType.NODE_UPDATED.getType(), repoEvents.get(2).getType()); + assertEquals(EventType.NODE_UPDATED.getType(), repoEvents.get(3).getType()); + assertEquals(EventType.CHILD_ASSOC_CREATED.getType(), repoEvents.get(4).getType()); } } diff --git a/repository/src/test/java/org/alfresco/repo/event2/EventConsolidatorUnitTest.java b/repository/src/test/java/org/alfresco/repo/event2/EventConsolidatorUnitTest.java index 21300eb054..42ba8f329b 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/EventConsolidatorUnitTest.java +++ b/repository/src/test/java/org/alfresco/repo/event2/EventConsolidatorUnitTest.java @@ -27,24 +27,38 @@ package org.alfresco.repo.event2; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.alfresco.model.ContentModel; +import org.alfresco.repo.event.v1.model.EventType; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.junit.Before; import org.junit.Test; public class EventConsolidatorUnitTest { - private NodeResourceHelper nodeResourceHelper = mock(NodeResourceHelper.class); - + private final NodeResourceHelper nodeResourceHelper = mock(NodeResourceHelper.class); + private NodeEventConsolidator eventConsolidator; + + @Before + public void setUp() throws Exception + { + eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); + } + @Test public void testGetMappedAspectsBeforeRemovedAndAddedEmpty() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); - Set currentAspects = new HashSet<>(); currentAspects.add("cm:geographic"); currentAspects.add("cm:auditable"); @@ -57,7 +71,6 @@ public class EventConsolidatorUnitTest @Test public void testGetMappedAspectsBefore_AspectRemoved() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); Set currentAspects = new HashSet<>(); @@ -79,7 +92,6 @@ public class EventConsolidatorUnitTest @Test public void testGetMappedAspectsBefore_AspectAdded() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); Set currentAspects = new HashSet<>(); @@ -102,7 +114,6 @@ public class EventConsolidatorUnitTest @Test public void testGetMappedAspectsBefore_AspectAddedAndRemoved() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); Set currentAspects = new HashSet<>(); @@ -125,7 +136,6 @@ public class EventConsolidatorUnitTest @Test public void testGetMappedAspectsBefore_AspectRemovedAndAdded() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS); @@ -150,8 +160,6 @@ public class EventConsolidatorUnitTest @Test public void testGetMappedAspectsBefore_AspectAddedTwiceRemovedOnce() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); - eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); @@ -178,8 +186,6 @@ public class EventConsolidatorUnitTest @Test public void testGetMappedAspectsBefore_AspectRemovedTwiceAddedOnce() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); - eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); @@ -206,7 +212,6 @@ public class EventConsolidatorUnitTest @Test public void testGetMappedAspectsBefore_FilteredAspectAdded() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.addAspect(ContentModel.ASPECT_COPIEDFROM); Set currentAspects = new HashSet<>(); @@ -227,7 +232,6 @@ public class EventConsolidatorUnitTest @Test public void testAddAspect() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); assertEquals(1, eventConsolidator.getAspectsAdded().size()); @@ -238,7 +242,6 @@ public class EventConsolidatorUnitTest @Test public void testRemoveAspect() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS); assertEquals(0, eventConsolidator.getAspectsAdded().size()); @@ -249,7 +252,6 @@ public class EventConsolidatorUnitTest @Test public void testAddAspectRemoveAspect() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS); @@ -260,7 +262,6 @@ public class EventConsolidatorUnitTest @Test public void testRemoveAspectAddAspect() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); @@ -271,7 +272,6 @@ public class EventConsolidatorUnitTest @Test public void testAddAspectTwiceRemoveAspectOnce() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); @@ -284,7 +284,6 @@ public class EventConsolidatorUnitTest @Test public void testAddAspectOnceRemoveAspectTwice() { - NodeEventConsolidator eventConsolidator = new NodeEventConsolidator(nodeResourceHelper); eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS); eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS); @@ -293,4 +292,83 @@ public class EventConsolidatorUnitTest assertEquals(1, eventConsolidator.getAspectsRemoved().size()); assertTrue(eventConsolidator.getAspectsRemoved().contains(ContentModel.ASSOC_CONTAINS)); } + + @Test + public void testOnMoveNodeWithPrimaryParent() + { + ChildAssociationRef oldAssociationMock = mock(ChildAssociationRef.class); + ChildAssociationRef newAssociationMock = mock(ChildAssociationRef.class); + NodeRef parentRefMock = mock(NodeRef.class); + given(newAssociationMock.isPrimary()).willReturn(true); + given(oldAssociationMock.getParentRef()).willReturn(parentRefMock); + + eventConsolidator.onMoveNode(oldAssociationMock, newAssociationMock); + + then(newAssociationMock).should().getChildRef(); + then(newAssociationMock).should().isPrimary(); + then(newAssociationMock).shouldHaveNoMoreInteractions(); + then(nodeResourceHelper).should().getPrimaryHierarchy(parentRefMock, true); + assertTrue("Node event consolidator should contain event type: UPDATED", eventConsolidator.getEventTypes().contains(EventType.NODE_UPDATED)); + + } + + @Test + public void testOnMoveNodeAfterSecondaryParentAdded() + { + ChildAssociationRef oldAssociationMock = mock(ChildAssociationRef.class); + ChildAssociationRef newAssociationMock = mock(ChildAssociationRef.class); + NodeRef nodeRefMock = mock(NodeRef.class); + NodeRef parentRefMock = mock(NodeRef.class); + List secondaryParentsMock = mock(List.class); + given(newAssociationMock.isPrimary()).willReturn(false); + given(newAssociationMock.getChildRef()).willReturn(nodeRefMock); + given(newAssociationMock.getParentRef()).willReturn(parentRefMock); + given(parentRefMock.getId()).willReturn("parent-id"); + given(nodeResourceHelper.getSecondaryParents(any(NodeRef.class))).willReturn(secondaryParentsMock); + + // when + eventConsolidator.onMoveNode(oldAssociationMock, newAssociationMock); + + then(newAssociationMock).should().isPrimary(); + then(newAssociationMock).should(times(2)).getChildRef(); + then(newAssociationMock).should(times(2)).getParentRef(); + then(newAssociationMock).shouldHaveNoMoreInteractions(); + then(oldAssociationMock).shouldHaveNoInteractions(); + then(nodeResourceHelper).should().getSecondaryParents(nodeRefMock); + then(secondaryParentsMock).should().remove("parent-id"); + then(secondaryParentsMock).shouldHaveNoMoreInteractions(); + assertTrue("Node event consolidator should contain event type: UPDATED", eventConsolidator.getEventTypes().contains(EventType.NODE_UPDATED)); + assertEquals(secondaryParentsMock, eventConsolidator.getSecondaryParentsBefore()); + } + + @Test + public void testOnMoveNodeBeforeSecondaryParentRemoved() + { + ChildAssociationRef oldAssociationMock = mock(ChildAssociationRef.class); + ChildAssociationRef newAssociationMock = mock(ChildAssociationRef.class); + NodeRef nodeRefMock = mock(NodeRef.class); + NodeRef parentRefMock = mock(NodeRef.class); + List secondaryParentsMock = mock(List.class); + given(newAssociationMock.isPrimary()).willReturn(false); + given(newAssociationMock.getChildRef()).willReturn(nodeRefMock); + given(oldAssociationMock.getParentRef()).willReturn(parentRefMock); + given(parentRefMock.getId()).willReturn("parent-id"); + given(nodeResourceHelper.getSecondaryParents(any(NodeRef.class))).willReturn(secondaryParentsMock); + + // when + eventConsolidator.onMoveNode(oldAssociationMock, newAssociationMock); + + then(newAssociationMock).should().isPrimary(); + then(newAssociationMock).should(times(2)).getChildRef(); + then(newAssociationMock).should().getParentRef(); + then(newAssociationMock).shouldHaveNoMoreInteractions(); + then(oldAssociationMock).should(times(3)).getParentRef(); + then(oldAssociationMock).shouldHaveNoMoreInteractions(); + then(nodeResourceHelper).should().getSecondaryParents(nodeRefMock); + then(secondaryParentsMock).should().contains("parent-id"); + then(secondaryParentsMock).should().add("parent-id"); + then(secondaryParentsMock).shouldHaveNoMoreInteractions(); + assertTrue("Node event consolidator should contain event type: NODE_UPDATED", eventConsolidator.getEventTypes().contains(EventType.NODE_UPDATED)); + assertEquals(secondaryParentsMock, eventConsolidator.getSecondaryParentsBefore()); + } } diff --git a/repository/src/test/java/org/alfresco/repo/event2/NodeResourceHelperUnitTest.java b/repository/src/test/java/org/alfresco/repo/event2/NodeResourceHelperUnitTest.java index 9c905b8196..e08d05ba00 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/NodeResourceHelperUnitTest.java +++ b/repository/src/test/java/org/alfresco/repo/event2/NodeResourceHelperUnitTest.java @@ -28,14 +28,43 @@ package org.alfresco.repo.event2; import static org.alfresco.repo.event2.NodeResourceHelper.getLocalizedPropertiesBefore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; +@RunWith(MockitoJUnitRunner.class) public class NodeResourceHelperUnitTest { + @Mock + private NodeService nodeServiceMock; + + @InjectMocks + private NodeResourceHelper nodeResourceHelper; + + @Before + public void setUp() throws Exception + { + MockitoAnnotations.openMocks(this); + } + @Test public void shouldExtractOnlyRelevantPropertiesForBeforeNode() { @@ -111,4 +140,39 @@ public class NodeResourceHelperUnitTest return this; } } + + @Test + public void testGetSecondaryParents() + { + NodeRef nodeRefMock = mock(NodeRef.class); + NodeRef parentRefMock = mock(NodeRef.class); + ChildAssociationRef secondaryParentMock = mock(ChildAssociationRef.class); + given(nodeServiceMock.getParentAssocs(any(NodeRef.class))).willReturn(List.of(secondaryParentMock)); + given(secondaryParentMock.isPrimary()).willReturn(false); + given(secondaryParentMock.getParentRef()).willReturn(parentRefMock); + + // when + List secondaryParents = nodeResourceHelper.getSecondaryParents(nodeRefMock); + + then(nodeServiceMock).should().getParentAssocs(nodeRefMock); + then(nodeServiceMock).shouldHaveNoMoreInteractions(); + then(secondaryParentMock).should().isPrimary(); + then(secondaryParentMock).should().getParentRef(); + then(secondaryParentMock).shouldHaveNoMoreInteractions(); + then(parentRefMock).should().getId(); + then(parentRefMock).shouldHaveNoMoreInteractions(); + assertNotNull(secondaryParents); + } + + @Test + public void testGetNoneSecondaryParents() + { + NodeRef nodeRefMock = mock(NodeRef.class); + + // when + List secondaryParents = nodeResourceHelper.getSecondaryParents(nodeRefMock); + + assertNotNull(secondaryParents); + assertTrue(secondaryParents.isEmpty()); + } } diff --git a/scripts/ci/docker-compose/docker-compose.yaml b/scripts/ci/docker-compose/docker-compose.yaml index ab264e2daa..2f44436764 100644 --- a/scripts/ci/docker-compose/docker-compose.yaml +++ b/scripts/ci/docker-compose/docker-compose.yaml @@ -56,4 +56,4 @@ services: CLIENT_SSL_TRUST_STORE: "file:/tengineAIO.truststore" CLIENT_SSL_TRUST_STORE_PASSWORD: "password" - CLIENT_SSL_TRUST_STORE_TYPE: "JCEKS" \ No newline at end of file + CLIENT_SSL_TRUST_STORE_TYPE: "JCEKS"