/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2020-2022 Bell Canada.
- * Modifications Copyright (C) 2022 TechMahindra Ltd.
+ * Modifications Copyright (C) 2022-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.
import org.onap.cps.spi.exceptions.CpsAdminException;
import org.onap.cps.spi.exceptions.CpsPathException;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
+import org.onap.cps.spi.exceptions.DataNodeNotFoundExceptionBatch;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.DataNodeBuilder;
import org.onap.cps.spi.repository.AnchorRepository;
private void addNewChildDataNode(final String dataspaceName, final String anchorName,
final String parentNodeXpath, final DataNode newChild) {
final FragmentEntity parentFragmentEntity =
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
+ getFragmentEntity(dataspaceName, anchorName, parentNodeXpath);
final FragmentEntity newChildAsFragmentEntity =
convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(),
parentFragmentEntity.getAnchor(), newChild);
private void addChildrenDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> newChildren) {
final FragmentEntity parentFragmentEntity =
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
+ getFragmentEntity(dataspaceName, anchorName, parentNodeXpath);
final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size());
try {
newChildren.forEach(newChildAsDataNode -> {
}
@Override
- public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath,
- final FetchDescendantsOption fetchDescendantsOption) {
- final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath,
- fetchDescendantsOption);
- return toDataNode(fragmentEntity, fetchDescendantsOption);
+ public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
+ final String xpath,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ final String targetXpath = isRootXpath(xpath) ? xpath : CpsPathUtil.getNormalizedXpath(xpath);
+ final Collection<DataNode> dataNodes = getDataNodesForMultipleXpaths(dataspaceName, anchorName,
+ Collections.singletonList(targetXpath), fetchDescendantsOption);
+ if (dataNodes.isEmpty()) {
+ throw new DataNodeNotFoundException(dataspaceName, anchorName, xpath);
+ }
+ return dataNodes;
}
- private FragmentEntity getFragmentWithoutDescendantsByXpath(final String dataspaceName,
- final String anchorName,
- final String xpath) {
- return getFragmentByXpath(dataspaceName, anchorName, xpath, FetchDescendantsOption.OMIT_DESCENDANTS);
+ @Override
+ public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName,
+ final Collection<String> xpaths,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ final Collection<FragmentEntity> fragmentEntities = getFragmentEntities(dataspaceName, anchorName, xpaths);
+ return toDataNodes(fragmentEntities, fetchDescendantsOption);
+ }
+
+ private Collection<FragmentEntity> getFragmentEntities(final String dataspaceName, final String anchorName,
+ final Collection<String> xpaths) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+
+ final Collection<String> nonRootXpaths = new HashSet<>(xpaths);
+ final boolean haveRootXpath = nonRootXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath);
+
+ final Collection<String> normalizedXpaths = new HashSet<>(nonRootXpaths.size());
+ for (final String xpath : nonRootXpaths) {
+ try {
+ normalizedXpaths.add(CpsPathUtil.getNormalizedXpath(xpath));
+ } catch (final PathParsingException e) {
+ log.warn("Error parsing xpath \"{}\": {}", xpath, e.getMessage());
+ }
+ }
+ final Collection<FragmentEntity> fragmentEntities =
+ new HashSet<>(fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorEntity.getId(), normalizedXpaths));
+
+ if (haveRootXpath) {
+ final List<FragmentExtract> fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity,
+ anchorEntity);
+ fragmentEntities.addAll(FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts));
+ }
+
+ return fragmentEntities;
}
- private FragmentEntity getFragmentByXpath(final String dataspaceName, final String anchorName,
- final String xpath, final FetchDescendantsOption fetchDescendantsOption) {
+ private FragmentEntity getFragmentEntity(final String dataspaceName, final String anchorName, final String xpath) {
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
final FragmentEntity fragmentEntity;
.stream().findFirst().orElse(null);
} else {
final String normalizedXpath = getNormalizedXpath(xpath);
- if (FetchDescendantsOption.OMIT_DESCENDANTS.equals(fetchDescendantsOption)) {
- fragmentEntity =
- fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, normalizedXpath);
- } else {
- fragmentEntity = buildFragmentEntitiesFromFragmentExtracts(anchorEntity, normalizedXpath)
- .stream().findFirst().orElse(null);
- }
+ fragmentEntity =
+ fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, normalizedXpath);
}
if (fragmentEntity == null) {
throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName(), xpath);
}
fragmentEntities = fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery);
if (cpsPathQuery.hasAncestorAxis()) {
- fragmentEntities = getAncestorFragmentEntities(anchorEntity, cpsPathQuery, fragmentEntities);
+ fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities);
}
return createDataNodesFromProxiedFragmentEntities(fetchDescendantsOption, anchorEntity, fragmentEntities);
}
fragmentRepository.quickFindWithDescendants(anchorEntity.getId(), xpathRegex);
fragmentEntities = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts);
if (cpsPathQuery.hasAncestorAxis()) {
- fragmentEntities = getAncestorFragmentEntities(anchorEntity, cpsPathQuery, fragmentEntities);
+ fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities);
}
return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities);
}
- private Collection<FragmentEntity> getAncestorFragmentEntities(final AnchorEntity anchorEntity,
+ private Collection<FragmentEntity> getAncestorFragmentEntities(final int anchorId,
final CpsPathQuery cpsPathQuery,
- Collection<FragmentEntity> fragmentEntities) {
- final Set<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery);
- fragmentEntities = ancestorXpaths.isEmpty() ? Collections.emptyList()
- : fragmentRepository.findAllByAnchorAndXpathIn(anchorEntity, ancestorXpaths);
- return fragmentEntities;
+ final Collection<FragmentEntity> fragmentEntities) {
+ final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery);
+ return ancestorXpaths.isEmpty() ? Collections.emptyList()
+ : fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorId, ancestorXpaths);
}
private List<DataNode> createDataNodesFromProxiedFragmentEntities(
.withChildDataNodes(childDataNodes).build();
}
+ private Collection<DataNode> toDataNodes(final Collection<FragmentEntity> fragmentEntities,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ final Collection<DataNode> dataNodes = new ArrayList<>(fragmentEntities.size());
+ for (final FragmentEntity fragmentEntity : fragmentEntities) {
+ dataNodes.add(toDataNode(fragmentEntity, fetchDescendantsOption));
+ }
+ return dataNodes;
+ }
+
private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
final FetchDescendantsOption fetchDescendantsOption) {
if (fetchDescendantsOption.hasNext()) {
@Override
public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath,
- final Map<String, Serializable> leaves) {
- final FragmentEntity fragmentEntity = getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, xpath);
- fragmentEntity.setAttributes(jsonObjectMapper.asJsonString(leaves));
+ final Map<String, Serializable> updateLeaves) {
+ final FragmentEntity fragmentEntity = getFragmentEntity(dataspaceName, anchorName, xpath);
+ final String currentLeavesAsString = fragmentEntity.getAttributes();
+ final String mergedLeaves = mergeLeaves(updateLeaves, currentLeavesAsString);
+ fragmentEntity.setAttributes(mergedLeaves);
fragmentRepository.save(fragmentEntity);
}
@Override
public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
final DataNode dataNode) {
- final FragmentEntity fragmentEntity =
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, dataNode.getXpath());
+ final FragmentEntity fragmentEntity = getFragmentEntity(dataspaceName, anchorName, dataNode.getXpath());
updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode);
try {
fragmentRepository.save(fragmentEntity);
}
@Override
- public void updateDataNodesAndDescendants(final String dataspaceName,
- final String anchorName,
- final List<DataNode> dataNodes) {
-
- final Map<DataNode, FragmentEntity> dataNodeFragmentEntityMap = dataNodes.stream()
- .collect(Collectors.toMap(
- dataNode -> dataNode,
- dataNode ->
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, dataNode.getXpath())));
- dataNodeFragmentEntityMap.forEach(
- (dataNode, fragmentEntity) -> updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode));
+ public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
+ final List<DataNode> updatedDataNodes) {
+ final Map<String, DataNode> xpathToUpdatedDataNode = updatedDataNodes.stream()
+ .collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode));
+
+ final Collection<String> xpaths = xpathToUpdatedDataNode.keySet();
+ final Collection<FragmentEntity> existingFragmentEntities =
+ getFragmentEntities(dataspaceName, anchorName, xpaths);
+
+ for (final FragmentEntity existingFragmentEntity : existingFragmentEntities) {
+ final DataNode updatedDataNode = xpathToUpdatedDataNode.get(existingFragmentEntity.getXpath());
+ updateFragmentEntityAndDescendantsWithDataNode(existingFragmentEntity, updatedDataNode);
+ }
+
try {
- fragmentRepository.saveAll(dataNodeFragmentEntityMap.values());
+ fragmentRepository.saveAll(existingFragmentEntities);
} catch (final StaleStateException staleStateException) {
- retryUpdateDataNodesIndividually(dataspaceName, anchorName, dataNodeFragmentEntityMap.values());
+ retryUpdateDataNodesIndividually(dataspaceName, anchorName, existingFragmentEntities);
}
}
@Transactional
public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> newListElements) {
- final FragmentEntity parentEntity =
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final FragmentEntity parentEntity = getFragmentEntity(dataspaceName, anchorName, parentNodeXpath);
final String listElementXpathPrefix = getListElementXpathPrefix(newListElements);
final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath =
extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listElementXpathPrefix);
anchorEntity -> fragmentRepository.deleteByAnchorIn(Set.of(anchorEntity)));
}
+ @Override
+ @Transactional
+ public void deleteDataNodes(final String dataspaceName, final Collection<String> anchorNames) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final Collection<AnchorEntity> anchorEntities =
+ anchorRepository.findAllByDataspaceAndNameIn(dataspaceEntity, anchorNames);
+ fragmentRepository.deleteByAnchorIn(anchorEntities);
+ }
+
+ @Override
+ @Transactional
+ public void deleteDataNodes(final String dataspaceName, final String anchorName,
+ final Collection<String> xpathsToDelete) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+
+ final Collection<String> deleteChecklist = new HashSet<>(xpathsToDelete.size());
+ for (final String xpath : xpathsToDelete) {
+ try {
+ deleteChecklist.add(CpsPathUtil.getNormalizedXpath(xpath));
+ } catch (final PathParsingException e) {
+ log.debug("Error parsing xpath \"{}\": {}", xpath, e.getMessage());
+ }
+ }
+
+ final Collection<String> xpathsToExistingContainers =
+ fragmentRepository.findAllXpathByAnchorAndXpathIn(anchorEntity, deleteChecklist);
+ deleteChecklist.removeAll(xpathsToExistingContainers);
+
+ final Collection<String> xpathsToExistingLists = deleteChecklist.stream()
+ .filter(xpath -> fragmentRepository.existsByAnchorAndXpathStartsWith(anchorEntity, xpath + "["))
+ .collect(Collectors.toList());
+ deleteChecklist.removeAll(xpathsToExistingLists);
+
+ if (!deleteChecklist.isEmpty()) {
+ throw new DataNodeNotFoundExceptionBatch(dataspaceName, anchorName, deleteChecklist);
+ }
+
+ fragmentRepository.deleteByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingContainers);
+ fragmentRepository.deleteListsByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingLists);
+ }
+
@Override
@Transactional
public void deleteListDataNode(final String dataspaceName, final String anchorName,
} else {
parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(targetXpath);
}
- parentFragmentEntity = getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
+ parentFragmentEntity = getFragmentEntity(dataspaceName, anchorName, parentNodeXpath);
if (CpsPathUtil.isPathToListElement(targetXpath)) {
targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath);
} else {
private boolean deleteDataNode(final FragmentEntity parentFragmentEntity, final String targetXpath) {
final String normalizedTargetXpath = CpsPathUtil.getNormalizedXpath(targetXpath);
if (parentFragmentEntity.getXpath().equals(normalizedTargetXpath)) {
- fragmentRepository.delete(parentFragmentEntity);
+ fragmentRepository.deleteFragmentEntity(parentFragmentEntity.getId());
return true;
}
if (parentFragmentEntity.getChildFragments()
private static boolean isRootXpath(final String xpath) {
return "/".equals(xpath) || "".equals(xpath);
}
+
+ private String mergeLeaves(final Map<String, Serializable> updateLeaves, final String currentLeavesAsString) {
+ final Map<String, Serializable> currentLeavesAsMap = currentLeavesAsString.isEmpty()
+ ? new HashMap<>() : jsonObjectMapper.convertJsonString(currentLeavesAsString, Map.class);
+ currentLeavesAsMap.putAll(updateLeaves);
+ if (currentLeavesAsMap.isEmpty()) {
+ return "";
+ }
+ return jsonObjectMapper.asJsonString(currentLeavesAsMap);
+ }
}