diff options
12 files changed, 201 insertions, 236 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java index a056efd6ce..2d45103904 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java @@ -34,7 +34,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.inventory.models.YangResource; import org.onap.cps.ncmp.impl.dmi.DmiProperties; import org.onap.cps.ncmp.impl.dmi.DmiRestClient; @@ -50,7 +49,6 @@ import org.springframework.stereotype.Service; /** * Operations class for DMI Model. */ -@Slf4j @RequiredArgsConstructor @Service public class DmiModelOperations { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy index e479fffe87..d00d3ab8f6 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy @@ -44,7 +44,6 @@ abstract class DmiOperationsBaseSpec extends Specification { ObjectMapper spyObjectMapper = Spy() def yangModelCmHandle = new YangModelCmHandle() - def otherYangModelCmHandle = new YangModelCmHandle() def static dmiServiceName = 'myServiceName' def static cmHandleId = 'some-cm-handle' def static alternateId = 'alt-id-' + cmHandleId diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy index 2f13a9a483..f5c4ea4369 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy @@ -91,7 +91,7 @@ class ModuleSyncServiceSpec extends Specification { def yangModelCmHandle = createAdvisedCmHandle(moduleSetTag) and: 'the service returns a list of module references when queried with the specified attributes' mockCpsModuleService.getModuleReferencesByAttribute(*_) >> [new ModuleReference('module1', '1')] - and: 'exception occurs when try to store result' + and: 'exception occurs when trying to store result' def testException = new RuntimeException('test') mockCpsModuleService.createSchemaSetFromModules(*_) >> { throw testException } when: 'module sync is triggered' @@ -110,7 +110,7 @@ class ModuleSyncServiceSpec extends Specification { def 'Sync models for a cm handle with previously cached module set tag.'() { given: 'a cm handle to be synced' def yangModelCmHandle = createAdvisedCmHandle('cached-tag') - and: 'The module set tag exist in the private cache' + and: 'The module set tag exists in the private cache' def moduleReferences = [ new ModuleReference('module1','1') ] def cachedModuleDelta = new ModuleDelta(moduleReferences, [:]) objectUnderTest.privateModuleSetCache.put('cached-tag', cachedModuleDelta) @@ -125,7 +125,7 @@ class ModuleSyncServiceSpec extends Specification { def 'Attempt to sync using a module set tag already being processed by a different instance or thread.'() { given: 'a cm handle to be synced' def yangModelCmHandle = createAdvisedCmHandle('duplicateTag') - and: 'The module set tag already exist in the processing semaphore set' + and: 'The module set tag already exists in the processing semaphore set' mockModuleSetTagsBeingProcessed.add('duplicate-processing-tag') > false when: 'module sync is triggered' objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle) diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java index ed7dbecc18..b67d70847c 100644 --- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java @@ -180,12 +180,12 @@ public class CpsPathBuilder extends CpsPathBaseListener { if (!isStartOfExpression) { currentNormalizedPathBuilder.append(" ").append(getLastElement(booleanOperators)).append(" "); } - currentNormalizedPathBuilder.append("@") - .append(name) - .append(operator) - .append("'") - .append(value.toString().replace("'", "''")) - .append("'"); + currentNormalizedPathBuilder.append("@").append(name).append(operator); + if (operator.equals("=")) { + currentNormalizedPathBuilder.append(wrapValueInSingleQuotes(value)); + } else { + currentNormalizedPathBuilder.append(value); + } } private static String getLastElement(final List<String> listOfStrings) { @@ -202,6 +202,10 @@ public class CpsPathBuilder extends CpsPathBaseListener { } } + private static String wrapValueInSingleQuotes(final Object value) { + return "'" + value.toString().replace("'", "''") + "'"; + } + private static String stripFirstAndLastCharacter(final String wrappedString) { return wrappedString.substring(1, wrappedString.length() - 1); } diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy index 16430d2fa5..a1bf28977a 100644 --- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy +++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy @@ -87,10 +87,10 @@ class CpsPathQuerySpec extends Specification { 'yang container' | '/cps-path' || '/cps-path' 'descendant anywhere' | '//cps-path' || '//cps-path' 'descendant with leaf condition' | '//cps-path[@key=1]' || "//cps-path[@key='1']" - 'descendant with leaf condition has ">" operator' | '//cps-path[@key>9]' || "//cps-path[@key>'9']" - 'descendant with leaf condition has "<" operator' | '//cps-path[@key<10]' || "//cps-path[@key<'10']" - 'descendant with leaf condition has ">=" operator' | '//cps-path[@key>=8]' || "//cps-path[@key>='8']" - 'descendant with leaf condition has "<=" operator' | '//cps-path[@key<=12]' || "//cps-path[@key<='12']" + 'descendant with leaf condition has ">" operator' | '//cps-path[@key>9]' || "//cps-path[@key>9]" + 'descendant with leaf condition has "<" operator' | '//cps-path[@key<10]' || "//cps-path[@key<10]" + 'descendant with leaf condition has ">=" operator' | '//cps-path[@key>=8]' || "//cps-path[@key>=8]" + 'descendant with leaf condition has "<=" operator' | '//cps-path[@key<=12]' || "//cps-path[@key<=12]" 'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor::parent[@key=1]' || "//cps-path[@key='1']/ancestor::parent[@key='1']" 'parent & child' | '/parent/child' || '/parent/child' 'parent leaf of type Integer & child' | '/parent/child[@code=1]/child2' || "/parent/child[@code='1']/child2" diff --git a/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java index ecbe447226..bdbdc7cf36 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java @@ -116,26 +116,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } } - /** - * Convert DataNode object into Fragment and places the result in the fragments placeholder. Performs same action - * for all DataNode children recursively. - * - * @param anchorEntity anchorEntity - * @param dataNodeToBeConverted dataNode - * @return a Fragment built from current DataNode - */ - private FragmentEntity convertToFragmentWithAllDescendants(final AnchorEntity anchorEntity, - final DataNode dataNodeToBeConverted) { - final FragmentEntity parentFragment = toFragmentEntity(anchorEntity, dataNodeToBeConverted); - final Builder<FragmentEntity> childFragmentsImmutableSetBuilder = ImmutableSet.builder(); - for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) { - final FragmentEntity childFragment = convertToFragmentWithAllDescendants(anchorEntity, childDataNode); - childFragmentsImmutableSetBuilder.add(childFragment); - } - parentFragment.setChildFragments(childFragmentsImmutableSetBuilder.build()); - return parentFragment; - } - @Override public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<DataNode> newListElements) { @@ -150,53 +130,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService addChildrenDataNodes(anchorEntity, parentNodeXpath, dataNodes); } - private void addChildrenDataNodes(final AnchorEntity anchorEntity, final String parentNodeXpath, - final Collection<DataNode> newChildren) { - final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); - final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size()); - try { - for (final DataNode newChildAsDataNode : newChildren) { - final FragmentEntity newChildAsFragmentEntity = - convertToFragmentWithAllDescendants(anchorEntity, newChildAsDataNode); - newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); - fragmentEntities.add(newChildAsFragmentEntity); - } - fragmentRepository.saveAll(fragmentEntities); - } catch (final DataIntegrityViolationException dataIntegrityViolationException) { - log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations", - dataIntegrityViolationException, fragmentEntities.size()); - retrySavingEachChildIndividually(anchorEntity, parentNodeXpath, newChildren); - } - } - - private void addNewChildDataNode(final AnchorEntity anchorEntity, final String parentNodeXpath, - final DataNode newChild) { - final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); - final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, newChild); - newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); - try { - fragmentRepository.save(newChildAsFragmentEntity); - } catch (final DataIntegrityViolationException dataIntegrityViolationException) { - throw AlreadyDefinedException.forDataNodes(Collections.singletonList(newChild.getXpath()), - anchorEntity.getName()); - } - } - - private void retrySavingEachChildIndividually(final AnchorEntity anchorEntity, final String parentNodeXpath, - final Collection<DataNode> newChildren) { - final Collection<String> failedXpaths = new HashSet<>(); - for (final DataNode newChild : newChildren) { - try { - addNewChildDataNode(anchorEntity, parentNodeXpath, newChild); - } catch (final AlreadyDefinedException alreadyDefinedException) { - failedXpaths.add(newChild.getXpath()); - } - } - if (!failedXpaths.isEmpty()) { - throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName()); - } - } - @Override public void batchUpdateDataLeaves(final String dataspaceName, final String anchorName, final Map<String, Map<String, Serializable>> updatedLeavesPerXPath) { @@ -246,19 +179,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } } - private void logMissingXPaths(final Collection<String> xpaths, final Collection<FragmentEntity> - existingFragmentEntities) { - final Set<String> existingXPaths = existingFragmentEntities.stream().map(FragmentEntity::getXpath) - .collect(Collectors.toSet()); - - final Set<String> missingXPaths = xpaths.stream().filter(xpath -> !existingXPaths.contains(xpath)) - .collect(Collectors.toSet()); - - if (!missingXPaths.isEmpty()) { - log.warn("Cannot update data nodes: Target XPaths {} not found in DB.", missingXPaths); - } - } - private void retryUpdateDataNodesIndividually(final AnchorEntity anchorEntity, final Collection<FragmentEntity> fragmentEntities) { final Collection<String> failedXpaths = new HashSet<>(); @@ -306,14 +226,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService description = "Time taken to query data nodes") public List<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) { - final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName); - final CpsPathQuery cpsPathQuery; - try { - cpsPathQuery = CpsPathUtil.getCpsPathQuery(cpsPath); - } catch (final PathParsingException pathParsingException) { - throw new CpsPathException(pathParsingException.getMessage()); - } + final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); + final CpsPathQuery cpsPathQuery = getCpsPathQuery(cpsPath); Collection<FragmentEntity> fragmentEntities; fragmentEntities = fragmentRepository.findByAnchorAndCpsPath(anchorEntity, cpsPathQuery); @@ -321,8 +235,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity, ancestorXpaths); } - fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption, - fragmentEntities); return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); } @@ -333,12 +245,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final FetchDescendantsOption fetchDescendantsOption, final PaginationOption paginationOption) { final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final CpsPathQuery cpsPathQuery; - try { - cpsPathQuery = CpsPathUtil.getCpsPathQuery(cpsPath); - } catch (final PathParsingException e) { - throw new CpsPathException(e.getMessage()); - } + final CpsPathQuery cpsPathQuery = getCpsPathQuery(cpsPath); final List<Long> anchorIds; if (paginationOption == NO_PAGINATION) { @@ -359,22 +266,10 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } else { fragmentEntities = fragmentRepository.findByAnchorIdsAndXpathIn(anchorIds, ancestorXpaths); } - } - fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption, - fragmentEntities); return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); } - private List<DataNode> createDataNodesFromFragmentEntities(final FetchDescendantsOption fetchDescendantsOption, - final Collection<FragmentEntity> fragmentEntities) { - final List<DataNode> dataNodes = new ArrayList<>(fragmentEntities.size()); - for (final FragmentEntity fragmentEntity : fragmentEntities) { - dataNodes.add(toDataNode(fragmentEntity, fetchDescendantsOption)); - } - return Collections.unmodifiableList(dataNodes); - } - @Override public String startSession() { return sessionManager.startSession(); @@ -394,41 +289,11 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override public Integer countAnchorsForDataspaceAndCpsPath(final String dataspaceName, final String cpsPath) { final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName); - final CpsPathQuery cpsPathQuery; - try { - cpsPathQuery = CpsPathUtil.getCpsPathQuery(cpsPath); - } catch (final PathParsingException e) { - throw new CpsPathException(e.getMessage()); - } + final CpsPathQuery cpsPathQuery = getCpsPathQuery(cpsPath); final List<Long> anchorIdList = getAnchorIdsForPagination(dataspaceEntity, cpsPathQuery, NO_PAGINATION); return anchorIdList.size(); } - private DataNode toDataNode(final FragmentEntity fragmentEntity, - final FetchDescendantsOption fetchDescendantsOption) { - final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption); - Map<String, Serializable> leaves = new HashMap<>(); - if (fragmentEntity.getAttributes() != null) { - leaves = jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map.class); - } - return new DataNodeBuilder() - .withXpath(fragmentEntity.getXpath()) - .withLeaves(leaves) - .withDataspace(fragmentEntity.getAnchor().getDataspace().getName()) - .withAnchor(fragmentEntity.getAnchor().getName()) - .withChildDataNodes(childDataNodes).build(); - } - - private FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final DataNode dataNode) { - return FragmentEntity.builder() - .anchor(anchorEntity) - .xpath(dataNode.getXpath()) - .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves())) - .build(); - } - - - @Override @Transactional public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, @@ -487,18 +352,18 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final Collection<String> deleteChecklist = getNormalizedXpaths(xpathsToDelete); final Collection<String> xpathsToExistingContainers = - fragmentRepository.findAllXpathByAnchorAndXpathIn(anchorEntity, deleteChecklist); + fragmentRepository.findAllXpathByAnchorAndXpathIn(anchorEntity, deleteChecklist); if (onlySupportListDeletion) { final Collection<String> xpathsToExistingListElements = xpathsToExistingContainers.stream() - .filter(CpsPathUtil::isPathToListElement).toList(); + .filter(CpsPathUtil::isPathToListElement).toList(); deleteChecklist.removeAll(xpathsToExistingListElements); } else { deleteChecklist.removeAll(xpathsToExistingContainers); } final Collection<String> xpathsToExistingLists = deleteChecklist.stream() - .filter(xpath -> fragmentRepository.existsByAnchorAndXpathStartsWith(anchorEntity, xpath + "[")) - .toList(); + .filter(xpath -> fragmentRepository.existsByAnchorAndXpathStartsWith(anchorEntity, xpath + "[")) + .toList(); deleteChecklist.removeAll(xpathsToExistingLists); if (!deleteChecklist.isEmpty()) { @@ -531,7 +396,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final String normalizedXpath = getNormalizedXpath(targetXpath); try { deleteDataNodes(dataspaceName, anchorName, Collections.singletonList(normalizedXpath), - onlySupportListNodeDeletion); + onlySupportListNodeDeletion); } catch (final DataNodeNotFoundExceptionBatch dataNodeNotFoundExceptionBatch) { throw new DataNodeNotFoundException(dataspaceName, anchorName, targetXpath); } @@ -559,18 +424,110 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final Collection<String> xpaths, final FetchDescendantsOption fetchDescendantsOption) { final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); - Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpaths); - fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption, - fragmentEntities); + final Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpaths); return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities); } + + private void addChildrenDataNodes(final AnchorEntity anchorEntity, final String parentNodeXpath, + final Collection<DataNode> newChildren) { + final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); + final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size()); + try { + for (final DataNode newChildAsDataNode : newChildren) { + final FragmentEntity newChildAsFragmentEntity = + convertToFragmentWithAllDescendants(anchorEntity, newChildAsDataNode); + newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); + fragmentEntities.add(newChildAsFragmentEntity); + } + fragmentRepository.saveAll(fragmentEntities); + } catch (final DataIntegrityViolationException dataIntegrityViolationException) { + log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations", + dataIntegrityViolationException, fragmentEntities.size()); + retrySavingEachChildIndividually(anchorEntity, parentNodeXpath, newChildren); + } + } + + private void addNewChildDataNode(final AnchorEntity anchorEntity, final String parentNodeXpath, + final DataNode newChild) { + final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath); + final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, newChild); + newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId()); + try { + fragmentRepository.save(newChildAsFragmentEntity); + } catch (final DataIntegrityViolationException dataIntegrityViolationException) { + throw AlreadyDefinedException.forDataNodes(Collections.singletonList(newChild.getXpath()), + anchorEntity.getName()); + } + } + + private void retrySavingEachChildIndividually(final AnchorEntity anchorEntity, final String parentNodeXpath, + final Collection<DataNode> newChildren) { + final Collection<String> failedXpaths = new HashSet<>(); + for (final DataNode newChild : newChildren) { + try { + addNewChildDataNode(anchorEntity, parentNodeXpath, newChild); + } catch (final AlreadyDefinedException alreadyDefinedException) { + failedXpaths.add(newChild.getXpath()); + } + } + if (!failedXpaths.isEmpty()) { + throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName()); + } + } + + private FragmentEntity convertToFragmentWithAllDescendants(final AnchorEntity anchorEntity, + final DataNode dataNodeToBeConverted) { + final FragmentEntity parentFragment = toFragmentEntity(anchorEntity, dataNodeToBeConverted); + final Builder<FragmentEntity> childFragmentsImmutableSetBuilder = ImmutableSet.builder(); + for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) { + final FragmentEntity childFragment = convertToFragmentWithAllDescendants(anchorEntity, childDataNode); + childFragmentsImmutableSetBuilder.add(childFragment); + } + parentFragment.setChildFragments(childFragmentsImmutableSetBuilder.build()); + return parentFragment; + } + + private FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final DataNode dataNode) { + return FragmentEntity.builder() + .anchor(anchorEntity) + .xpath(dataNode.getXpath()) + .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves())) + .build(); + } + + private List<DataNode> createDataNodesFromFragmentEntities(final FetchDescendantsOption fetchDescendantsOption, + final Collection<FragmentEntity> fragmentEntities) { + final Collection<FragmentEntity> fragmentEntitiesWithDescendants = + fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption, fragmentEntities); + final List<DataNode> dataNodes = new ArrayList<>(fragmentEntitiesWithDescendants.size()); + for (final FragmentEntity fragmentEntity : fragmentEntitiesWithDescendants) { + dataNodes.add(toDataNode(fragmentEntity, fetchDescendantsOption)); + } + return Collections.unmodifiableList(dataNodes); + } + + private DataNode toDataNode(final FragmentEntity fragmentEntity, + final FetchDescendantsOption fetchDescendantsOption) { + final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption); + Map<String, Serializable> leaves = new HashMap<>(); + if (fragmentEntity.getAttributes() != null) { + leaves = jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map.class); + } + return new DataNodeBuilder() + .withXpath(fragmentEntity.getXpath()) + .withLeaves(leaves) + .withDataspace(fragmentEntity.getAnchor().getDataspace().getName()) + .withAnchor(fragmentEntity.getAnchor().getName()) + .withChildDataNodes(childDataNodes).build(); + } + private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity, final FetchDescendantsOption fetchDescendantsOption) { if (fetchDescendantsOption.hasNext()) { return fragmentEntity.getChildFragments().stream() .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption.next())) - .collect(Collectors.toList()); + .toList(); } return Collections.emptyList(); } @@ -585,36 +542,9 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return fragmentRepository.findAnchorIdsForPagination(dataspaceEntity, cpsPathQuery, paginationOption); } - private static String getNormalizedXpath(final String xpathSource) { - if (isRootXpath(xpathSource)) { - return xpathSource; - } - try { - return CpsPathUtil.getNormalizedXpath(xpathSource); - } catch (final PathParsingException pathParsingException) { - throw new CpsPathException(pathParsingException.getMessage()); - } - } - - private static Collection<String> getNormalizedXpaths(final Collection<String> xpaths) { - final Collection<String> normalizedXpaths = new HashSet<>(xpaths.size()); - for (final String xpath : xpaths) { - try { - normalizedXpaths.add(getNormalizedXpath(xpath)); - } catch (final CpsPathException cpsPathException) { - log.warn("Error parsing xpath \"{}\": {}", xpath, cpsPathException.getMessage()); - } - } - return normalizedXpaths; - } - private FragmentEntity getFragmentEntity(final AnchorEntity anchorEntity, final String xpath) { - final FragmentEntity fragmentEntity; - if (isRootXpath(xpath)) { - fragmentEntity = fragmentRepository.findOneByAnchorId(anchorEntity.getId()).orElse(null); - } else { - fragmentEntity = fragmentRepository.getByAnchorAndXpath(anchorEntity, getNormalizedXpath(xpath)); - } + final FragmentEntity fragmentEntity = + fragmentRepository.findByAnchorIdAndXpath(anchorEntity.getId(), getNormalizedXpath(xpath)); if (fragmentEntity == null) { throw new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath); } @@ -628,7 +558,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final boolean haveRootXpath = normalizedXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath); final List<FragmentEntity> fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity, - normalizedXpaths); + normalizedXpaths); for (final FragmentEntity fragmentEntity : fragmentEntities) { normalizedXpaths.remove(fragmentEntity.getXpath()); @@ -647,15 +577,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return fragmentEntities; } - private static String getListElementXpathPrefix(final Collection<DataNode> newListElements) { - if (newListElements.isEmpty()) { - throw new CpsAdminException("Invalid list replacement", - "Cannot replace list elements with empty collection"); - } - final String firstChildNodeXpath = newListElements.iterator().next().getXpath(); - return firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf('[') + 1); - } - private FragmentEntity getFragmentForReplacement(final FragmentEntity parentEntity, final DataNode newListElement, final FragmentEntity existingListElementEntity) { @@ -671,6 +592,28 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return existingListElementEntity; } + private String mergeLeaves(final Map<String, Serializable> updateLeaves, final String currentLeavesAsString) { + Map<String, Serializable> currentLeavesAsMap = new HashMap<>(); + if (currentLeavesAsString != null) { + currentLeavesAsMap = jsonObjectMapper.convertJsonString(currentLeavesAsString, Map.class); + currentLeavesAsMap.putAll(updateLeaves); + } + + if (currentLeavesAsMap.isEmpty()) { + return ""; + } + return jsonObjectMapper.asJsonString(currentLeavesAsMap); + } + + private void copyAttributesFromNewDataNode(final FragmentEntity existingFragmentEntity, + final DataNode newDataNode) { + final String oldOrderedLeavesAsJson = getOrderedLeavesAsJson(existingFragmentEntity.getAttributes()); + final String newOrderedLeavesAsJson = getOrderedLeavesAsJson(newDataNode.getLeaves()); + if (!oldOrderedLeavesAsJson.equals(newOrderedLeavesAsJson)) { + existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves())); + } + } + private String getOrderedLeavesAsJson(final Map<String, Serializable> currentLeaves) { final Map<String, Serializable> sortedLeaves = new TreeMap<>(String::compareTo); sortedLeaves.putAll(currentLeaves); @@ -686,6 +629,38 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return jsonObjectMapper.asJsonString(sortedLeaves); } + private static String getNormalizedXpath(final String xpathSource) { + if (isRootXpath(xpathSource)) { + return xpathSource; + } + try { + return CpsPathUtil.getNormalizedXpath(xpathSource); + } catch (final PathParsingException pathParsingException) { + throw new CpsPathException(pathParsingException.getMessage()); + } + } + + private static Collection<String> getNormalizedXpaths(final Collection<String> xpaths) { + final Collection<String> normalizedXpaths = new HashSet<>(xpaths.size()); + for (final String xpath : xpaths) { + try { + normalizedXpaths.add(getNormalizedXpath(xpath)); + } catch (final CpsPathException cpsPathException) { + log.warn("Error parsing xpath \"{}\": {}", xpath, cpsPathException.getMessage()); + } + } + return normalizedXpaths; + } + + private static String getListElementXpathPrefix(final Collection<DataNode> newListElements) { + if (newListElements.isEmpty()) { + throw new CpsAdminException("Invalid list replacement", + "Cannot replace list elements with empty collection"); + } + final String firstChildNodeXpath = newListElements.iterator().next().getXpath(); + return firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf('[') + 1); + } + private static Map<String, FragmentEntity> extractListElementFragmentEntitiesByXPath( final Set<FragmentEntity> childEntities, final String listElementXpathPrefix) { return childEntities.stream() @@ -717,25 +692,23 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath()); } - private void copyAttributesFromNewDataNode(final FragmentEntity existingFragmentEntity, - final DataNode newDataNode) { - final String oldOrderedLeavesAsJson = getOrderedLeavesAsJson(existingFragmentEntity.getAttributes()); - final String newOrderedLeavesAsJson = getOrderedLeavesAsJson(newDataNode.getLeaves()); - if (!oldOrderedLeavesAsJson.equals(newOrderedLeavesAsJson)) { - existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves())); + private static CpsPathQuery getCpsPathQuery(final String cpsPath) { + try { + return CpsPathUtil.getCpsPathQuery(cpsPath); + } catch (final PathParsingException e) { + throw new CpsPathException(e.getMessage()); } } - private String mergeLeaves(final Map<String, Serializable> updateLeaves, final String currentLeavesAsString) { - Map<String, Serializable> currentLeavesAsMap = new HashMap<>(); - if (currentLeavesAsString != null) { - currentLeavesAsMap = jsonObjectMapper.convertJsonString(currentLeavesAsString, Map.class); - currentLeavesAsMap.putAll(updateLeaves); - } - - if (currentLeavesAsMap.isEmpty()) { - return ""; + private static void logMissingXPaths(final Collection<String> xpaths, + final Collection<FragmentEntity> existingFragmentEntities) { + final Set<String> existingXPaths = + existingFragmentEntities.stream().map(FragmentEntity::getXpath).collect(Collectors.toSet()); + final Set<String> missingXPaths = + xpaths.stream().filter(xpath -> !existingXPaths.contains(xpath)).collect(Collectors.toSet()); + if (!missingXPaths.isEmpty()) { + log.warn("Cannot update data nodes: Target XPaths {} not found in DB.", missingXPaths); } - return jsonObjectMapper.asJsonString(currentLeavesAsMap); } + } diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java index 9598230cdb..a8c1fd2d4e 100755 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java @@ -25,12 +25,10 @@ package org.onap.cps.ri.repository; import java.util.Collection; import java.util.List; -import java.util.Optional; import org.onap.cps.ri.models.AnchorEntity; import org.onap.cps.ri.models.DataspaceEntity; import org.onap.cps.ri.models.FragmentEntity; import org.onap.cps.ri.utils.EscapeUtils; -import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -41,12 +39,8 @@ import org.springframework.stereotype.Repository; public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, FragmentRepositoryCpsPathQuery, FragmentPrefetchRepository { - Optional<FragmentEntity> findByAnchorAndXpath(AnchorEntity anchorEntity, String xpath); - - default FragmentEntity getByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) { - return findByAnchorAndXpath(anchorEntity, xpath).orElseThrow(() -> - new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath)); - } + @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath = :xpath", nativeQuery = true) + FragmentEntity findByAnchorIdAndXpath(@Param("anchorId") long anchorId, @Param("xpath") String xpath); @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath IN (:xpaths)", nativeQuery = true) @@ -84,9 +78,6 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, List<FragmentEntity> findByAnchorIdsAndXpathIn(@Param("anchorIds") Collection<Long> anchorIds, @Param("xpaths") Collection<String> xpaths); - @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId LIMIT 1", nativeQuery = true) - Optional<FragmentEntity> findOneByAnchorId(@Param("anchorId") long anchorId); - @Modifying @Query(value = "DELETE FROM fragment WHERE anchor_id IN (:anchorIds)", nativeQuery = true) void deleteByAnchorIdIn(@Param("anchorIds") Collection<Long> anchorIds); diff --git a/cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy index 36bf55e2db..500fe76a90 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy @@ -246,7 +246,7 @@ class CpsDataPersistenceServiceImplSpec extends Specification { def createDataNodeAndMockRepositoryMethodSupportingIt(xpath, scenario) { def dataNode = new DataNodeBuilder().withXpath(xpath).build() def fragmentEntity = new FragmentEntity(xpath: xpath, childFragments: []) - mockFragmentRepository.getByAnchorAndXpath(_, xpath) >> fragmentEntity + mockFragmentRepository.findByAnchorIdAndXpath(_, xpath) >> fragmentEntity if ('EXCEPTION' == scenario) { mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") } } diff --git a/csit/plans/cps/test.properties b/csit/plans/cps/test.properties index e7b9519c2d..52e82bdb85 100644 --- a/csit/plans/cps/test.properties +++ b/csit/plans/cps/test.properties @@ -21,7 +21,7 @@ DMI_SERVICE_URL=http://$LOCAL_IP:$DMI_PORT DOCKER_REPO=nexus3.onap.org:10003 CPS_VERSION=latest -DMI_VERSION=1.5.1-SNAPSHOT-latest +DMI_VERSION=latest ADVISED_MODULES_SYNC_SLEEP_TIME_MS=2000 CMHANDLE_DATA_SYNC_SLEEP_TIME_MS=2000 diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index feb58d849d..ae34fc3606 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -142,7 +142,7 @@ services: ncmp-dmi-plugin-demo-and-csit-stub: container_name: ${NCMP_DMI_PLUGIN_DEMO_AND_CSIT_STUB_CONTAINER_NAME:-ncmp-dmi-plugin-demo-and-csit-stub} - image: ${DOCKER_REPO:-nexus3.onap.org:10003}/onap/dmi-plugin-demo-and-csit-stub:${DMI_DEMO_STUB_VERSION:-latest} + image: ${DOCKER_REPO:-nexus3.onap.org:10003}/onap/dmi-stub:${DMI_DEMO_STUB_VERSION:-latest} ports: - ${DMI_DEMO_STUB_PORT:-8784}:8092 environment: diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy index 02a10cfa6b..aea5c10a93 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy @@ -266,9 +266,9 @@ abstract class CpsIntegrationSpecBase extends Specification { def registerSequenceOfCmHandlesWithManyModuleReferencesButDoNotWaitForReady(dmiPlugin, moduleSetTag, numberOfCmHandles, offset) { def cmHandles = [] def id = offset - def moduleReferences = (1..200).collect { moduleSetTag + '_Module_' + it.toString() } + def moduleReferences = (1..200).collect { "${moduleSetTag}Module${it}" } (1..numberOfCmHandles).each { - def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch-'+id, moduleSetTag: moduleSetTag, alternateId: NO_ALTERNATE_ID) + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: "ch-${id}", moduleSetTag: moduleSetTag, alternateId: NO_ALTERNATE_ID) cmHandles.add(ncmpServiceCmHandle) dmiDispatcher1.moduleNamesPerCmHandleId[ncmpServiceCmHandle.cmHandleId] = moduleReferences dmiDispatcher2.moduleNamesPerCmHandleId[ncmpServiceCmHandle.cmHandleId] = moduleReferences diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy index 963bc1fe61..20fa546eba 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/ModuleSyncWatchdogIntegrationSpec.groovy @@ -132,7 +132,7 @@ class ModuleSyncWatchdogIntegrationSpec extends CpsIntegrationSpecBase { } def logInstrumentation(timer, description) { - System.out.println('*** CPS-2478, ' + description + ' : ' + timer.count()+ ' times, total ' + timer.totalTime(TimeUnit.MILLISECONDS) + ' ms') + println "*** CPS-2478, $description : Invoked ${timer.count()} times, Total Time: ${timer.totalTime(TimeUnit.MILLISECONDS)} ms, Mean Time: ${timer.mean(TimeUnit.MILLISECONDS)} ms" return true } |