From 5b719d1cdc23da0c95310933fee09753401075f9 Mon Sep 17 00:00:00 2001 From: niamhcore Date: Tue, 19 Jan 2021 12:45:44 +0000 Subject: [PATCH] Add another Child to a Fragment that has already at least one Child Issue-ID: CPS-164 Signed-off-by: niamhcore Change-Id: Ib6a4f8ce4fc9247726e016ecaa0b1e45ff11bdbb --- .../spi/impl/CpsDataPersistenceServiceImpl.java | 37 ++++-- .../cps/spi/repository/FragmentRepository.java | 13 ++ .../spi/impl/CpsDataPersistenceServiceTest.java | 140 +++++++++++++-------- cps-ri/src/test/resources/data/fragment.sql | 9 +- .../onap/cps/spi/CpsDataPersistenceService.java | 11 ++ 5 files changed, 147 insertions(+), 63 deletions(-) diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index ef64c34d5c..c73b65ddd8 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -48,6 +48,18 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService private static final Gson GSON = new GsonBuilder().create(); + @Override + public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentXpath, + final DataNode dataNode) { + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); + final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); + final FragmentEntity parentFragment = + fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, parentXpath); + final FragmentEntity childFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNode); + parentFragment.getChildFragments().add(childFragment); + fragmentRepository.save(parentFragment); + } + @Override public void storeDataNode(final String dataspaceName, final String anchorName, final DataNode dataNode) { final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); @@ -68,21 +80,26 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService */ private static FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity, final AnchorEntity anchorEntity, final DataNode dataNodeToBeConverted) { - final FragmentEntity parentFragment = FragmentEntity.builder() - .dataspace(dataspaceEntity) - .anchor(anchorEntity) - .xpath(dataNodeToBeConverted.getXpath()) - .attributes(GSON.toJson(dataNodeToBeConverted.getLeaves())) - .build(); - - final Builder fragmentEntityBuilder = ImmutableSet.builder(); + final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted); + final Builder childFragmentsImmutableSetBuilder = ImmutableSet.builder(); for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) { final FragmentEntity childFragment = convertToFragmentWithAllDescendants(parentFragment.getDataspace(), parentFragment.getAnchor(), childDataNode); - fragmentEntityBuilder.add(childFragment); + childFragmentsImmutableSetBuilder.add(childFragment); } - parentFragment.setChildFragments(fragmentEntityBuilder.build()); + parentFragment.setChildFragments(childFragmentsImmutableSetBuilder.build()); return parentFragment; } + + private static FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity, + final AnchorEntity anchorEntity, + final DataNode dataNode) { + return FragmentEntity.builder() + .dataspace(dataspaceEntity) + .anchor(anchorEntity) + .xpath(dataNode.getXpath()) + .attributes(GSON.toJson(dataNode.getLeaves())) + .build(); + } } diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java index 4d44943963..6fc956c44d 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java @@ -22,9 +22,13 @@ package org.onap.cps.spi.repository; import java.util.Collection; +import java.util.Optional; import javax.validation.constraints.NotNull; +import org.checkerframework.checker.nullness.qual.NonNull; import org.onap.cps.spi.entities.AnchorEntity; +import org.onap.cps.spi.entities.DataspaceEntity; import org.onap.cps.spi.entities.FragmentEntity; +import org.onap.cps.spi.exceptions.NotFoundInDataspaceException; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -34,6 +38,15 @@ import org.springframework.stereotype.Repository; @Repository public interface FragmentRepository extends JpaRepository { + Optional findByDataspaceAndAnchorAndXpath(@NonNull DataspaceEntity dataspaceEntity, + @NonNull AnchorEntity anchorEntity, @NonNull String xpath); + + default FragmentEntity getByDataspaceAndAnchorAndXpath(@NonNull DataspaceEntity dataspaceEntity, + @NonNull AnchorEntity anchorEntity, @NonNull String xpath) { + return findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, xpath) + .orElseThrow(() -> new NotFoundInDataspaceException(dataspaceEntity.getName(), xpath)); + } + @Modifying @Query("DELETE FROM FragmentEntity fe WHERE fe.anchor IN (:anchors)") void deleteByAnchorIn(@NotNull @Param("anchors") Collection anchorEntities); diff --git a/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceTest.java b/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceTest.java index db2941c19f..de0942c3c3 100644 --- a/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceTest.java +++ b/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceTest.java @@ -22,7 +22,9 @@ package org.onap.cps.spi.impl; import static junit.framework.TestCase.assertEquals; import com.google.common.collect.ImmutableSet; +import java.util.Arrays; import java.util.Collections; +import org.assertj.core.api.Assertions; import org.junit.ClassRule; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,6 +33,7 @@ import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.entities.FragmentEntity; import org.onap.cps.spi.exceptions.AnchorNotFoundException; import org.onap.cps.spi.exceptions.DataspaceNotFoundException; +import org.onap.cps.spi.exceptions.NotFoundInDataspaceException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.repository.FragmentRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -47,22 +50,23 @@ public class CpsDataPersistenceServiceTest { private static final String CLEAR_DATA = "/data/clear-all.sql"; private static final String SET_DATA = "/data/fragment.sql"; - private static final String NON_EXISTING_DATASPACE_NAME = "NON EXISTING DATASPACE"; private static final String DATASPACE_NAME = "DATASPACE-001"; private static final String ANCHOR_NAME1 = "ANCHOR-001"; - private static final String NON_EXISTING_ANCHOR_NAME = "NON EXISTING ANCHOR"; - private static final String PARENT_XPATH = "/parent"; - private static final String CHILD_XPATH = "/parent/child"; - private static final String GRAND_CHILD_XPATH = "/parent/child/grandchild"; - private static final String PARENT_XPATH_NEW = "/parent-new"; - private static final String CHILD_XPATH_NEW = "/parent/child-new"; - private static final String GRAND_CHILD_XPATH_NEW = "/parent/child/grandchild-new"; - private static final long PARENT_ID = 3001; - private static final long CHILD_ID = 3002; - private static final long GRAND_CHILD_ID = 3003; - private static final long PARENT_ID_NEW = 2; - private static final long CHILD_ID_NEW = 3; - private static final long GRAND_CHILD_ID_NEW = 4; + + private static final long PARENT_ID_4001 = 4001; + private static final long PARENT_ID_4002 = 4002; + private static final long PARENT_ID_4003 = 4003; + private static final String PARENT_XPATH1 = "/parent-1"; + private static final String PARENT_XPATH2 = "/parent-2"; + private static final String PARENT_XPATH3 = "/parent-3"; + + private static final long CHILD_ID_4004 = 4004; + private static final String CHILD_XPATH1 = "/parent-1/child-1"; + private static final String CHILD_XPATH2 = "/parent-2/child-2"; + + private static final long GRAND_CHILD_ID_4006 = 4006; + private static final String GRAND_CHILD_XPATH1 = "/parent-1/child-1/grandchild-1"; + @ClassRule public static DatabaseTestContainer databaseTestContainer = DatabaseTestContainer.getInstance(); @@ -76,48 +80,90 @@ public class CpsDataPersistenceServiceTest { @Test @Sql({CLEAR_DATA, SET_DATA}) public void testGetFragmentsWithChildAndGrandChild() { - final FragmentEntity parentFragment = fragmentRepository.findById(PARENT_ID).orElseThrow(); - final FragmentEntity childFragment = fragmentRepository.findById(CHILD_ID).orElseThrow(); - final FragmentEntity grandChildFragment = fragmentRepository.findById(GRAND_CHILD_ID).orElseThrow(); - - assertFragment(parentFragment, childFragment, grandChildFragment, PARENT_XPATH, CHILD_XPATH, GRAND_CHILD_XPATH); + final FragmentEntity parentFragment = fragmentRepository.findById(PARENT_ID_4001).orElseThrow(); + final FragmentEntity childFragment = fragmentRepository.findById(CHILD_ID_4004).orElseThrow(); + final FragmentEntity grandChildFragment = fragmentRepository.findById(GRAND_CHILD_ID_4006).orElseThrow(); + assertFragment(parentFragment, childFragment, grandChildFragment, PARENT_XPATH1, CHILD_XPATH1, + GRAND_CHILD_XPATH1); } @Test(expected = DataspaceNotFoundException.class) @Sql({CLEAR_DATA, SET_DATA}) public void testStoreDataNodeAtNonExistingDataspace() { cpsDataPersistenceService - .storeDataNode(NON_EXISTING_DATASPACE_NAME, ANCHOR_NAME1, - createDataNodeWithChildAndGrandChild(PARENT_XPATH_NEW, CHILD_XPATH_NEW, GRAND_CHILD_XPATH_NEW)); + .storeDataNode("Non Existing Dataspace Name", ANCHOR_NAME1, new DataNode()); } @Test(expected = AnchorNotFoundException.class) @Sql({CLEAR_DATA, SET_DATA}) public void testStoreDataNodeAtNonExistingAnchor() { cpsDataPersistenceService - .storeDataNode(DATASPACE_NAME, NON_EXISTING_ANCHOR_NAME, - createDataNodeWithChildAndGrandChild(PARENT_XPATH_NEW, CHILD_XPATH_NEW, GRAND_CHILD_XPATH_NEW)); + .storeDataNode(DATASPACE_NAME, "Non Existing Anchor Name", new DataNode()); } @Test(expected = DataIntegrityViolationException.class) @Sql({CLEAR_DATA, SET_DATA}) public void testStoreDataNodeWithIntegrityException() { cpsDataPersistenceService.storeDataNode(DATASPACE_NAME, ANCHOR_NAME1, - createDataNodeWithChildAndGrandChild(PARENT_XPATH, CHILD_XPATH, GRAND_CHILD_XPATH)); + createDataNodeTree(PARENT_XPATH1)); } @Test @Sql({CLEAR_DATA, SET_DATA}) - public void testStoreDataNodeWithChildrenAndGrandChildren() { + public void testStoreDataNodeWithChildAndGrandChild() { + final String parentXpath = "/parent-new"; + final String childXpath = "/parent-new/child-new"; + final String grandChildXpath = "/parent-new/child-new/grandchild-new"; + cpsDataPersistenceService.storeDataNode(DATASPACE_NAME, ANCHOR_NAME1, - createDataNodeWithChildAndGrandChild(PARENT_XPATH_NEW, CHILD_XPATH_NEW, GRAND_CHILD_XPATH_NEW)); + createDataNodeTree(parentXpath, childXpath, grandChildXpath)); + final FragmentEntity parentFragment = getFragmentByXpath(parentXpath); + final FragmentEntity childFragment = getFragmentByXpath(childXpath); + final FragmentEntity grandChildFragment = getFragmentByXpath(grandChildXpath); + assertFragment(parentFragment, childFragment, grandChildFragment, parentXpath, childXpath, + grandChildXpath); + } + + @Test + @Sql({CLEAR_DATA, SET_DATA}) + public void testAddChildToFragmentThatHasOneChild() { + final String childXpath = "some-xpath"; + final DataNode childDataNode = createDataNodeTree(childXpath); + cpsDataPersistenceService + .addChildDataNode(DATASPACE_NAME, ANCHOR_NAME1, PARENT_XPATH2, childDataNode); + final FragmentEntity parentFragment = fragmentRepository.findById(PARENT_ID_4002).orElseThrow(); + Assertions.assertThat(parentFragment.getChildFragments()) + .hasSize(2) + .extracting(FragmentEntity::getXpath) + .containsExactlyInAnyOrder(childXpath, CHILD_XPATH2); + } + + @Test + @Sql({CLEAR_DATA, SET_DATA}) + public void testAddChildToFragmentThatHasNoChild() { + final String childXpath = "some-xpath"; + final DataNode childDataNode = createDataNodeTree(childXpath); + cpsDataPersistenceService + .addChildDataNode(DATASPACE_NAME, ANCHOR_NAME1, PARENT_XPATH3, childDataNode); + final FragmentEntity parentFragment = fragmentRepository.findById(PARENT_ID_4003).orElseThrow(); + Assertions.assertThat(parentFragment.getChildFragments()) + .hasSize(1) + .extracting(FragmentEntity::getXpath) + .containsExactlyInAnyOrder(childXpath); + } - final FragmentEntity parentFragment = fragmentRepository.findById(PARENT_ID_NEW).orElseThrow(); - final FragmentEntity childFragment = fragmentRepository.findById(CHILD_ID_NEW).orElseThrow(); - final FragmentEntity grandChildFragment = fragmentRepository.findById(GRAND_CHILD_ID_NEW).orElseThrow(); + @Test(expected = DataIntegrityViolationException.class) + @Sql({CLEAR_DATA, SET_DATA}) + public void testAddAChildWithTheSameXpathAsExistingChild() { + cpsDataPersistenceService + .addChildDataNode(DATASPACE_NAME, ANCHOR_NAME1, PARENT_XPATH1, createDataNodeTree(CHILD_XPATH1)); + } - assertFragment(parentFragment, childFragment, grandChildFragment, PARENT_XPATH_NEW, CHILD_XPATH_NEW, - GRAND_CHILD_XPATH_NEW); + @Test(expected = NotFoundInDataspaceException.class) + @Sql({CLEAR_DATA, SET_DATA}) + public void testAddAChildWithToAParentThatDoesNotExist() { + cpsDataPersistenceService + .addChildDataNode(DATASPACE_NAME, ANCHOR_NAME1, "non-existing-xpath", createDataNodeTree("some-xpath")); } private void assertFragment(final FragmentEntity parentFragment, final FragmentEntity childFragment, @@ -136,24 +182,18 @@ public class CpsDataPersistenceServiceTest { assertEquals(ANCHOR_NAME1, grandChildFragment.getAnchor().getName()); } - private DataNode createDataNodeWithChildAndGrandChild(final String parentXpath, final String childXpath, - final String grandChildXpath) { - final DataNode parentDataNode = DataNode.builder() - .xpath(parentXpath) - .build(); - - final DataNode childDataNode = DataNode.builder() - .xpath(childXpath) - .childDataNodes(Collections.emptySet()) - .build(); - - final DataNode grandChildDataNode = DataNode.builder() - .xpath(grandChildXpath) - .childDataNodes(Collections.emptySet()) - .build(); - - parentDataNode.setChildDataNodes(ImmutableSet.of(childDataNode)); - childDataNode.setChildDataNodes(ImmutableSet.of(grandChildDataNode)); - return parentDataNode; + private FragmentEntity getFragmentByXpath(final String xpath) { + return fragmentRepository.findAll().stream() + .filter(fragment -> fragment.getXpath().contains(xpath)).findAny().orElseThrow(); + } + + private static DataNode createDataNodeTree(final String... xpaths) { + final DataNode dataNode = DataNode.builder().xpath(xpaths[0]).childDataNodes(Collections.emptySet()).build(); + if (xpaths.length > 1) { + final String[] xPathsDescendant = Arrays.copyOfRange(xpaths, 1, xpaths.length); + final DataNode childDataNode = createDataNodeTree(xPathsDescendant); + dataNode.setChildDataNodes(ImmutableSet.of(childDataNode)); + } + return dataNode; } } diff --git a/cps-ri/src/test/resources/data/fragment.sql b/cps-ri/src/test/resources/data/fragment.sql index c50f595306..05f5bfe455 100644 --- a/cps-ri/src/test/resources/data/fragment.sql +++ b/cps-ri/src/test/resources/data/fragment.sql @@ -8,6 +8,9 @@ INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES (3001, 'ANCHOR-001', 1001, 2001); INSERT INTO FRAGMENT (ID, XPATH, ANCHOR_ID, PARENT_ID, DATASPACE_ID) VALUES - (3001, '/parent', 3001, null, 1001), - (3002, '/parent/child', 3001, 3001, 1001), - (3003, '/parent/child/grandchild', 3001, 3002, 1001); \ No newline at end of file + (4001, '/parent-1', 3001, null, 1001), + (4002, '/parent-2', 3001, null, 1001), + (4003, '/parent-3', 3001, null, 1001), + (4004, '/parent-1/child-1', 3001, 4001, 1001), + (4005, '/parent-2/child-2', 3001, 4002, 1001), + (4006, '/parent-1/child-1/grandchild-1', 3001, 4004, 1001); \ No newline at end of file diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java index 50ece0e27b..d59fa47467 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java @@ -39,4 +39,15 @@ public interface CpsDataPersistenceService { */ void storeDataNode(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull DataNode dataNode); + + /** + * Add a child to a Fragment. + * + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @param parentXpath parent xpath + * @param dataNode dataNode + */ + void addChildDataNode(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentXpath, + @NonNull DataNode dataNode); } -- 2.16.6