diff options
author | rajesh.kumar <rk00747546@techmahindra.com> | 2023-04-21 17:48:44 +0530 |
---|---|---|
committer | rajesh.kumar <rk00747546@techmahindra.com> | 2023-04-21 17:48:44 +0530 |
commit | ce3451d10315b54238b886491779b3975f326e02 (patch) | |
tree | 95c577b9babdf79acb0d2a7329a9180ffc0c6c51 | |
parent | a3697108f9e0ea6ab27b2bda799656de1689a3df (diff) |
Fix issues related to query across all anchors
CPS-1580: Query Across All Anchors Does NOT Filter on Dataspace
CPS-1582: NullPointerException in queryDataNodesAcrossAnchors
Issue-ID: CPS-1580
Change-ID: I73f97f986a817d423f93a8d922dcd9647b2104ab
Signed-off-by: rajesh.kumar <rk00747546@techmahindra.com>
11 files changed, 103 insertions, 36 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java b/cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java index b89342827..3b0e1834e 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +33,7 @@ import javax.persistence.Table; import javax.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -46,12 +48,14 @@ import lombok.Setter; @Builder @Entity @Table(name = "anchor") +@EqualsAndHashCode(onlyExplicitlyIncluded = true) public class AnchorEntity implements Serializable { private static final long serialVersionUID = -8049987915308262518L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @EqualsAndHashCode.Include private Integer id; @NotNull diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java b/cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java index 593746d94..30906ade3 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java @@ -2,6 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2020-2021 Nordix Foundation. * Modifications Copyright (C) 2020-2021 Pantheon.tech + * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +31,7 @@ import javax.persistence.Id; import javax.persistence.Table; import javax.validation.constraints.NotNull; import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -44,12 +46,14 @@ import lombok.Setter; @AllArgsConstructor @NoArgsConstructor @Table(name = "dataspace") +@EqualsAndHashCode(onlyExplicitlyIncluded = true) public class DataspaceEntity implements Serializable { private static final long serialVersionUID = 8395254649813051882L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @EqualsAndHashCode.Include private Integer id; @NotNull diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java b/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java index 82afc5a81..b90fb799a 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java @@ -2,6 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2020-2023 Nordix Foundation. * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -84,10 +85,12 @@ public class FragmentEntity implements Serializable { @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dataspace_id") + @EqualsAndHashCode.Include private DataspaceEntity dataspace; @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "anchor_id") + @EqualsAndHashCode.Include private AnchorEntity anchor; @ToString.Exclude diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntityArranger.java b/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntityArranger.java index 55d3c7e87..a33c8d055 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntityArranger.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntityArranger.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Nordix Foundation + * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +48,25 @@ public class FragmentEntityArranger { return reuniteChildrenWithTheirParents(fragmentEntityPerId); } + /** + * Convert a collection of (related) FragmentExtracts into FragmentEntities (trees) with descendants. + * + * @param fragmentExtracts FragmentExtracts to convert. + * @param fragmentExtractAnchorMap Map of fragmentExtract with their anchor. + * @return a collection of FragmentEntities (trees) with descendants. + */ + public static Collection<FragmentEntity> toFragmentEntityTreesAcrossAnchors( + final Collection<FragmentExtract> fragmentExtracts, + final Map<Long, AnchorEntity> fragmentExtractAnchorMap) { + final Map<Long, FragmentEntity> fragmentEntityPerId = new HashMap<>(); + for (final FragmentExtract fragmentExtract : fragmentExtracts) { + final AnchorEntity anchorEntity = fragmentExtractAnchorMap.get(fragmentExtract.getId()); + final FragmentEntity fragmentEntity = toFragmentEntity(anchorEntity, fragmentExtract); + fragmentEntityPerId.put(fragmentEntity.getId(), fragmentEntity); + } + return reuniteChildrenWithTheirParents(fragmentEntityPerId); + } + private static FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final FragmentExtract fragmentExtract) { final FragmentEntity fragmentEntity = new FragmentEntity(); 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 aa631d1b1..0a77de234 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 @@ -324,17 +324,20 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } catch (final PathParsingException e) { throw new CpsPathException(e.getMessage()); } - + final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); Collection<FragmentEntity> fragmentEntities; if (canUseRegexQuickFind(fetchDescendantsOption, cpsPathQuery)) { - return getDataNodesUsingRegexQuickFind(fetchDescendantsOption, anchorEntity, cpsPathQuery); + return (anchorEntity == ALL_ANCHORS) ? getDataNodesUsingRegexQuickFindAcrossAnchors(fetchDescendantsOption, + dataspaceEntity, cpsPathQuery) : getDataNodesUsingRegexQuickFind(fetchDescendantsOption, + anchorEntity, cpsPathQuery); } - fragmentEntities = (anchorEntity == ALL_ANCHORS) ? fragmentRepository.findByCpsPath(cpsPathQuery) + fragmentEntities = (anchorEntity == ALL_ANCHORS) ? fragmentRepository + .findByDataspaceAndCpsPath(dataspaceEntity.getId(), cpsPathQuery) : fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery); if (cpsPathQuery.hasAncestorAxis()) { final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); - fragmentEntities = (anchorEntity == ALL_ANCHORS) ? getAncestorFragmentEntitiesAcrossAnchors(cpsPathQuery, - fragmentEntities) : getFragmentEntities(anchorEntity, ancestorXpaths, fetchDescendantsOption); + fragmentEntities = (anchorEntity == ALL_ANCHORS) ? getAncestorFragmentEntitiesAcrossAnchors(dataspaceEntity, + cpsPathQuery, fragmentEntities) : getFragmentEntities(anchorEntity, ancestorXpaths, fetchDescendantsOption); } return createDataNodesFromProxiedFragmentEntities(fetchDescendantsOption, anchorEntity, fragmentEntities); } @@ -357,22 +360,48 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final CpsPathQuery cpsPathQuery) { Collection<FragmentEntity> fragmentEntities; final String xpathRegex = FragmentQueryBuilder.getXpathSqlRegex(cpsPathQuery, true); - final List<FragmentExtract> fragmentExtracts = (anchorEntity == ALL_ANCHORS) - ? fragmentRepository.quickFindWithDescendantsAcrossAnchor(xpathRegex) : + final List<FragmentExtract> fragmentExtracts = fragmentRepository.quickFindWithDescendants(anchorEntity.getId(), xpathRegex); fragmentEntities = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts); if (cpsPathQuery.hasAncestorAxis()) { final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); - fragmentEntities = (anchorEntity == ALL_ANCHORS) ? getAncestorFragmentEntitiesAcrossAnchors(cpsPathQuery, - fragmentEntities) : getFragmentEntities(anchorEntity, ancestorXpaths, fetchDescendantsOption); + fragmentEntities = getFragmentEntities(anchorEntity, ancestorXpaths, fetchDescendantsOption); } return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); } - private Collection<FragmentEntity> getAncestorFragmentEntitiesAcrossAnchors(final CpsPathQuery cpsPathQuery, - final Collection<FragmentEntity> fragmentEntities) { + private Collection<FragmentEntity> getAncestorFragmentEntitiesAcrossAnchors(final DataspaceEntity dataspaceEntity, + final CpsPathQuery cpsPathQuery, final Collection<FragmentEntity> fragmentEntities) { final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); - return ancestorXpaths.isEmpty() ? Collections.emptyList() : fragmentRepository.findAllByXpathIn(ancestorXpaths); + return ancestorXpaths.isEmpty() ? Collections.emptyList() : fragmentRepository + .findAllByXpathIn(dataspaceEntity, ancestorXpaths); + } + + private List<DataNode> getDataNodesUsingRegexQuickFindAcrossAnchors( + final FetchDescendantsOption fetchDescendantsOption, final DataspaceEntity dataspaceEntity, + final CpsPathQuery cpsPathQuery) { + Collection<FragmentEntity> fragmentEntities; + final String xpathRegex = FragmentQueryBuilder.getXpathSqlRegex(cpsPathQuery, true); + final List<FragmentExtract> fragmentExtracts = fragmentRepository + .quickFindWithDescendantsAcrossAnchor(dataspaceEntity.getId(), xpathRegex); + final Map<Long, AnchorEntity> fragmentExtractAnchorMap = getFragmentExtractAnchorMap(fragmentExtracts); + fragmentEntities = FragmentEntityArranger.toFragmentEntityTreesAcrossAnchors(fragmentExtracts, + fragmentExtractAnchorMap); + if (cpsPathQuery.hasAncestorAxis()) { + fragmentEntities = getAncestorFragmentEntitiesAcrossAnchors(dataspaceEntity, + cpsPathQuery, fragmentEntities); + } + return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); + } + + private Map<Long, AnchorEntity> getFragmentExtractAnchorMap(final List<FragmentExtract> fragmentExtracts) { + final Map<Long, AnchorEntity> fragmentEntityAnchorMap = new HashMap<>(); + fragmentExtracts.forEach(fragmentExtract -> { + final AnchorEntity anchorEntity = anchorRepository.getById(Math.toIntExact(fragmentExtract.getAnchorId())); + fragmentEntityAnchorMap.put(fragmentExtract.getId(), anchorEntity); + } + ); + return fragmentEntityAnchorMap; } private List<DataNode> createDataNodesFromProxiedFragmentEntities( diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java index c23159593..5716304a1 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java @@ -75,15 +75,17 @@ public class FragmentQueryBuilder { } /** - * Create a sql query to retrieve by cps path. + * Create a sql query to retrieve by dataspace id and cps path. * * @param cpsPathQuery the cps path query to be transformed into a sql query * @return a executable query object */ - public Query getQueryForCpsPath(final CpsPathQuery cpsPathQuery) { - final StringBuilder sqlStringBuilder = new StringBuilder("SELECT * FROM FRAGMENT WHERE xpath ~ :xpathRegex"); + public Query getQueryForDataspaceAndCpsPath(final int dataspaceId, final CpsPathQuery cpsPathQuery) { + final StringBuilder sqlStringBuilder = new StringBuilder("SELECT * FROM FRAGMENT WHERE dataspace_id = " + + ":dataspaceId AND xpath ~ :xpathRegex"); final Map<String, Object> queryParameters = new HashMap<>(); final String xpathRegex = getXpathSqlRegex(cpsPathQuery, false); + queryParameters.put("dataspaceId", dataspaceId); queryParameters.put("xpathRegex", xpathRegex); if (cpsPathQuery.hasLeafConditions()) { sqlStringBuilder.append(" AND attributes @> :leafDataAsJson\\:\\:jsonb"); 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 d486a39c7..0067cfe43 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 @@ -27,6 +27,7 @@ import java.util.Collection; import java.util.List;
import java.util.Optional;
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.entities.FragmentExtract;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
@@ -54,7 +55,9 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, List<FragmentEntity> findAllByAnchorAndXpathIn(AnchorEntity anchorEntity, Collection<String> xpath);
- List<FragmentEntity> findAllByXpathIn(Collection<String> xpath);
+ @Query("SELECT f FROM FragmentEntity f WHERE dataspace = :dataspace AND xpath IN :xpaths")
+ List<FragmentEntity> findAllByXpathIn(@Param("dataspace") DataspaceEntity dataspace,
+ @Param("xpaths") Collection<String> xpaths);
@Modifying
@Query("DELETE FROM FragmentEntity WHERE anchor IN (:anchors)")
@@ -109,7 +112,8 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
+ " CAST(attributes AS TEXT) AS attributes"
- + " FROM FRAGMENT WHERE xpath ~ :xpathRegex",
+ + " FROM FRAGMENT WHERE dataspace_id = :dataspaceId AND xpath ~ :xpathRegex",
nativeQuery = true)
- List<FragmentExtract> quickFindWithDescendantsAcrossAnchor(@Param("xpathRegex") String xpathRegex);
+ List<FragmentExtract> quickFindWithDescendantsAcrossAnchor(@Param("dataspaceId") int dataspaceId,
+ @Param("xpathRegex") String xpathRegex);
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java index 32041e7d5..4195f62a6 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java @@ -28,5 +28,5 @@ import org.onap.cps.spi.entities.FragmentEntity; public interface FragmentRepositoryCpsPathQuery { List<FragmentEntity> findByAnchorAndCpsPath(int anchorId, CpsPathQuery cpsPathQuery); - List<FragmentEntity> findByCpsPath(CpsPathQuery cpsPathQuery); + List<FragmentEntity> findByDataspaceAndCpsPath(int dataspaceId, CpsPathQuery cpsPathQuery); } diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java index b95491cd3..3ea5cef83 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java @@ -51,8 +51,8 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps @Override @Transactional - public List<FragmentEntity> findByCpsPath(final CpsPathQuery cpsPathQuery) { - final Query query = fragmentQueryBuilder.getQueryForCpsPath(cpsPathQuery); + public List<FragmentEntity> findByDataspaceAndCpsPath(final int dataspaceId, final CpsPathQuery cpsPathQuery) { + final Query query = fragmentQueryBuilder.getQueryForDataspaceAndCpsPath(dataspaceId, cpsPathQuery); final List<FragmentEntity> fragmentEntities = query.getResultList(); log.debug("Fetched {} fragment entities by cps path across all anchors.", fragmentEntities.size()); return fragmentEntities; diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy index 60aaa8114..fae2c5aae 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy @@ -193,6 +193,7 @@ class CpsDataPersistenceQueryDataNodeSpec extends CpsPersistenceSpecBase { 'String and no descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@title="Dune"]' | OMIT_DESCENDANTS || 2 || ['ANCHOR-004', 'ANCHOR-005'] 'Integer and descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@price=5]' | INCLUDE_ALL_DESCENDANTS || 3 || ['ANCHOR-004', 'ANCHOR-005'] 'No condition no descendants' | '/shops/shop[@id=1]/categories' | OMIT_DESCENDANTS || 6 || ['ANCHOR-004', 'ANCHOR-005'] + 'top node and all descendants' | '/shops' | INCLUDE_ALL_DESCENDANTS || 2 || ['ANCHOR-004', 'ANCHOR-005'] 'multiple list-ancestors' | '//book/ancestor::categories' | INCLUDE_ALL_DESCENDANTS || 4 || ['ANCHOR-004', 'ANCHOR-005'] 'one ancestor with list value' | '//book/ancestor::categories[@code=1]' | INCLUDE_ALL_DESCENDANTS || 2 || ['ANCHOR-004', 'ANCHOR-005'] 'list with index value in the xpath prefix' | '//categories[@code=1]/book/ancestor::shop[@id=1]' | INCLUDE_ALL_DESCENDANTS || 2 || ['ANCHOR-004', 'ANCHOR-005'] diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy index f02aa754f..fcd8f4637 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy @@ -212,32 +212,32 @@ class CpsDataPersistenceServiceSpec extends Specification { when: 'replace data node tree' objectUnderTest.updateDataNodesAndDescendants('dataspaceName', 'anchorName', dataNodes) then: 'call fragment repository save all method' - 1 * mockFragmentRepository.saveAll({fragmentEntities -> assert fragmentEntities as List == expectedFragmentEntities}) + 1 * mockFragmentRepository.saveAll({fragmentEntities -> fragmentEntities.containsAll(expectedFragmentEntities)}) where: 'the following Data Type is passed' scenario | dataNodes || expectedFragmentEntities 'empty data node list' | [] || [] - 'one data node in list' | [new DataNode(xpath: '/test/xpath', leaves: ['id': 'testId'], childDataNodes: [])] || [new FragmentEntity(xpath: '/test/xpath', attributes: '{"id":"testId"}', childFragments: [])] + 'one data node in list' | [new DataNode(xpath: '/test/xpath', leaves: ['id': 'testId'], childDataNodes: [])] || [new FragmentEntity(xpath: '/test/xpath', attributes: '{"id":"testId"}', childFragments: [], anchor: new AnchorEntity(id: 123), dataspace: new DataspaceEntity(id: 1))] } def 'update data nodes and descendants'() { given: 'the fragment repository returns fragment entities related to the xpath inputs' - mockFragmentRepository.findExtractsWithDescendants(123, ['/test/xpath1', '/test/xpath2'] as Set, _) >> [ + mockFragmentRepository.findExtractsWithDescendants(123, ['/test/xpath1', '/test/xpath2'] as Set, _) >> [ mockFragmentExtract(1, null, 123, '/test/xpath1', null), mockFragmentExtract(2, null, 123, '/test/xpath2', null) - ] + ] and: 'some data nodes with descendants' - def dataNode1 = new DataNode(xpath: '/test/xpath1', leaves: ['id': 'testId1'], childDataNodes: [new DataNode(xpath: '/test/xpath1/child', leaves: ['id': 'childTestId1'])]) - def dataNode2 = new DataNode(xpath: '/test/xpath2', leaves: ['id': 'testId2'], childDataNodes: [new DataNode(xpath: '/test/xpath2/child', leaves: ['id': 'childTestId2'])]) + def dataNode1 = new DataNode(xpath: '/test/xpath1', leaves: ['id': 'testId1'], childDataNodes: [new DataNode(xpath: '/test/xpath1/child', leaves: ['id': 'childTestId1'])]) + def dataNode2 = new DataNode(xpath: '/test/xpath2', leaves: ['id': 'testId2'], childDataNodes: [new DataNode(xpath: '/test/xpath2/child', leaves: ['id': 'childTestId2'])]) when: 'the fragment entities are update by the data nodes' - objectUnderTest.updateDataNodesAndDescendants('dataspaceName', 'anchorName', [dataNode1, dataNode2]) + objectUnderTest.updateDataNodesAndDescendants('dataspaceName', 'anchorName', [dataNode1, dataNode2]) then: 'call fragment repository save all method is called with the updated fragments' - 1 * mockFragmentRepository.saveAll({fragmentEntities -> { - fragmentEntities.containsAll([ - new FragmentEntity(xpath: '/test/xpath1', attributes: '{"id":"testId1"}', childFragments: [new FragmentEntity(xpath: '/test/xpath1/child', attributes: '{"id":"childTestId1"}', childFragments: [])]), - new FragmentEntity(xpath: '/test/xpath2', attributes: '{"id":"testId2"}', childFragments: [new FragmentEntity(xpath: '/test/xpath2/child', attributes: '{"id":"childTestId2"}', childFragments: [])]) - ]) - assert fragmentEntities.size() == 2 - }}) + 1 * mockFragmentRepository.saveAll({fragmentEntities -> { + fragmentEntities.containsAll([ + new FragmentEntity(xpath: '/test/xpath1', anchor: new AnchorEntity(id: 123), dataspace: new DataspaceEntity(id: 1), attributes: '{"id":"testId1"}', childFragments: [new FragmentEntity(xpath: '/test/xpath1/child', attributes: '{"id":"childTestId1"}', childFragments: [])]), + new FragmentEntity(xpath: '/test/xpath2', anchor: new AnchorEntity(id: 123), dataspace: new DataspaceEntity(id: 1), attributes: '{"id":"testId2"}', childFragments: [new FragmentEntity(xpath: '/test/xpath2/child', attributes: '{"id":"childTestId2"}', childFragments: [])]) + ]) + assert fragmentEntities.size() == 2 + }}) } def createDataNodeAndMockRepositoryMethodSupportingIt(xpath, scenario) { @@ -261,7 +261,7 @@ class CpsDataPersistenceServiceSpec extends Specification { dataNodes.add(dataNode) def fragmentExtract = mockFragmentExtract(fragmentId, null, null, xpath, null) fragmentExtracts.add(fragmentExtract) - def fragmentEntity = new FragmentEntity(id: fragmentId, xpath: xpath, childFragments: []) + def fragmentEntity = new FragmentEntity(id: fragmentId, xpath: xpath, childFragments: [], anchor: new AnchorEntity(id: 123), dataspace: new DataspaceEntity(id: 1)) mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, xpath) >> fragmentEntity if ('EXCEPTION' == scenario) { mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") } |