2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 Nordix Foundation
4 * Modifications Copyright (C) 2020-2022 Bell Canada.
5 * Modifications Copyright (C) 2021 Pantheon.tech
6 * Modifications Copyright (C) 2022-2024 TechMahindra Ltd.
7 * Modifications Copyright (C) 2022 Deutsche Telekom AG
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * SPDX-License-Identifier: Apache-2.0
22 * ============LICENSE_END=========================================================
25 package org.onap.cps.api.impl;
27 import io.micrometer.core.annotation.Timed;
28 import java.io.Serializable;
29 import java.time.OffsetDateTime;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.List;
36 import java.util.stream.Collectors;
37 import lombok.RequiredArgsConstructor;
38 import lombok.extern.slf4j.Slf4j;
39 import org.onap.cps.api.CpsAnchorService;
40 import org.onap.cps.api.CpsDataService;
41 import org.onap.cps.api.CpsDeltaService;
42 import org.onap.cps.cpspath.parser.CpsPathUtil;
43 import org.onap.cps.events.CpsDataUpdateEventsService;
44 import org.onap.cps.events.model.Data.Operation;
45 import org.onap.cps.spi.CpsDataPersistenceService;
46 import org.onap.cps.spi.FetchDescendantsOption;
47 import org.onap.cps.spi.exceptions.DataValidationException;
48 import org.onap.cps.spi.model.Anchor;
49 import org.onap.cps.spi.model.DataNode;
50 import org.onap.cps.spi.model.DataNodeBuilder;
51 import org.onap.cps.spi.model.DeltaReport;
52 import org.onap.cps.spi.utils.CpsValidator;
53 import org.onap.cps.utils.ContentType;
54 import org.onap.cps.utils.DataMapUtils;
55 import org.onap.cps.utils.JsonObjectMapper;
56 import org.onap.cps.utils.PrefixResolver;
57 import org.onap.cps.utils.YangParser;
58 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
59 import org.springframework.stereotype.Service;
63 @RequiredArgsConstructor
64 public class CpsDataServiceImpl implements CpsDataService {
66 private static final String ROOT_NODE_XPATH = "/";
67 private static final long DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS = 300L;
68 private static final String NO_DATA_NODES = "No data nodes.";
70 private final CpsDataPersistenceService cpsDataPersistenceService;
71 private final CpsDataUpdateEventsService cpsDataUpdateEventsService;
72 private final CpsAnchorService cpsAnchorService;
74 private final CpsValidator cpsValidator;
75 private final YangParser yangParser;
76 private final CpsDeltaService cpsDeltaService;
77 private final JsonObjectMapper jsonObjectMapper;
78 private final PrefixResolver prefixResolver;
81 public void saveData(final String dataspaceName, final String anchorName, final String nodeData,
82 final OffsetDateTime observedTimestamp) {
83 saveData(dataspaceName, anchorName, nodeData, observedTimestamp, ContentType.JSON);
87 @Timed(value = "cps.data.service.datanode.root.save",
88 description = "Time taken to save a root data node")
89 public void saveData(final String dataspaceName, final String anchorName, final String nodeData,
90 final OffsetDateTime observedTimestamp, final ContentType contentType) {
91 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
92 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
93 final Collection<DataNode> dataNodes =
94 buildDataNodesWithParentNodeXpath(anchor, ROOT_NODE_XPATH, nodeData, contentType);
95 cpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, dataNodes);
96 sendDataUpdatedEvent(anchor, ROOT_NODE_XPATH, Operation.CREATE, observedTimestamp);
100 public void saveData(final String dataspaceName, final String anchorName, final String parentNodeXpath,
101 final String nodeData, final OffsetDateTime observedTimestamp) {
102 saveData(dataspaceName, anchorName, parentNodeXpath, nodeData, observedTimestamp, ContentType.JSON);
106 @Timed(value = "cps.data.service.datanode.child.save",
107 description = "Time taken to save a child data node")
108 public void saveData(final String dataspaceName, final String anchorName, final String parentNodeXpath,
109 final String nodeData, final OffsetDateTime observedTimestamp,
110 final ContentType contentType) {
111 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
112 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
113 final Collection<DataNode> dataNodes =
114 buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType);
115 cpsDataPersistenceService.addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes);
116 sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.CREATE, observedTimestamp);
120 @Timed(value = "cps.data.service.list.element.save",
121 description = "Time taken to save list elements")
122 public void saveListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
123 final String jsonData, final OffsetDateTime observedTimestamp) {
124 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
125 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
126 final Collection<DataNode> listElementDataNodeCollection =
127 buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON);
128 if (isRootNodeXpath(parentNodeXpath)) {
129 cpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, listElementDataNodeCollection);
131 cpsDataPersistenceService.addListElements(dataspaceName, anchorName, parentNodeXpath,
132 listElementDataNodeCollection);
134 sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp);
138 @Timed(value = "cps.data.service.datanode.get",
139 description = "Time taken to get data nodes for an xpath")
140 public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
142 final FetchDescendantsOption fetchDescendantsOption) {
143 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
144 return cpsDataPersistenceService.getDataNodes(dataspaceName, anchorName, xpath, fetchDescendantsOption);
148 @Timed(value = "cps.data.service.datanode.batch.get",
149 description = "Time taken to get a batch of data nodes")
150 public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName,
151 final Collection<String> xpaths,
152 final FetchDescendantsOption fetchDescendantsOption) {
153 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
154 return cpsDataPersistenceService.getDataNodesForMultipleXpaths(dataspaceName, anchorName, xpaths,
155 fetchDescendantsOption);
159 @Timed(value = "cps.data.service.datanode.leaves.update",
160 description = "Time taken to update a batch of leaf data nodes")
161 public void updateNodeLeaves(final String dataspaceName, final String anchorName, final String parentNodeXpath,
162 final String nodeData, final OffsetDateTime observedTimestamp, final ContentType contentType) {
163 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
164 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
165 final Collection<DataNode> dataNodesInPatch =
166 buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType);
167 final Map<String, Map<String, Serializable>> xpathToUpdatedLeaves = dataNodesInPatch.stream()
168 .collect(Collectors.toMap(DataNode::getXpath, DataNode::getLeaves));
169 cpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, xpathToUpdatedLeaves);
170 sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp);
174 @Timed(value = "cps.data.service.datanode.leaves.descendants.leaves.update",
175 description = "Time taken to update data node leaves and existing descendants leaves")
176 public void updateNodeLeavesAndExistingDescendantLeaves(final String dataspaceName, final String anchorName,
177 final String parentNodeXpath,
178 final String dataNodeUpdatesAsJson,
179 final OffsetDateTime observedTimestamp) {
180 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
181 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
182 final Collection<DataNode> dataNodeUpdates =
183 buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, dataNodeUpdatesAsJson, ContentType.JSON);
184 for (final DataNode dataNodeUpdate : dataNodeUpdates) {
185 processDataNodeUpdate(anchor, dataNodeUpdate);
187 sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp);
191 public String startSession() {
192 return cpsDataPersistenceService.startSession();
196 public void closeSession(final String sessionId) {
197 cpsDataPersistenceService.closeSession(sessionId);
201 public void lockAnchor(final String sessionID, final String dataspaceName, final String anchorName) {
202 lockAnchor(sessionID, dataspaceName, anchorName, DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS);
206 public void lockAnchor(final String sessionID, final String dataspaceName,
207 final String anchorName, final Long timeoutInMilliseconds) {
208 cpsDataPersistenceService.lockAnchor(sessionID, dataspaceName, anchorName, timeoutInMilliseconds);
212 @Timed(value = "cps.data.service.get.delta",
213 description = "Time taken to get delta between anchors")
214 public List<DeltaReport> getDeltaByDataspaceAndAnchors(final String dataspaceName,
215 final String sourceAnchorName,
216 final String targetAnchorName, final String xpath,
217 final FetchDescendantsOption fetchDescendantsOption) {
219 final Collection<DataNode> sourceDataNodes = getDataNodesForMultipleXpaths(dataspaceName,
220 sourceAnchorName, Collections.singletonList(xpath), fetchDescendantsOption);
221 final Collection<DataNode> targetDataNodes = getDataNodesForMultipleXpaths(dataspaceName,
222 targetAnchorName, Collections.singletonList(xpath), fetchDescendantsOption);
224 return cpsDeltaService.getDeltaReports(sourceDataNodes, targetDataNodes);
227 @Timed(value = "cps.data.service.get.deltaBetweenAnchorAndPayload",
228 description = "Time taken to get delta between anchor and a payload")
230 public List<DeltaReport> getDeltaByDataspaceAnchorAndPayload(final String dataspaceName,
231 final String sourceAnchorName, final String xpath,
232 final Map<String, String> yangResourcesNameToContentMap,
233 final String targetData,
234 final FetchDescendantsOption fetchDescendantsOption) {
236 final Anchor sourceAnchor = cpsAnchorService.getAnchor(dataspaceName, sourceAnchorName);
238 final Collection<DataNode> sourceDataNodes = getDataNodes(dataspaceName,
239 sourceAnchorName, xpath, fetchDescendantsOption);
241 final Collection<DataNode> sourceDataNodesRebuilt =
242 new ArrayList<>(rebuildSourceDataNodes(xpath, sourceAnchor, sourceDataNodes));
244 final Collection<DataNode> targetDataNodes =
245 new ArrayList<>(buildTargetDataNodes(sourceAnchor, xpath, yangResourcesNameToContentMap, targetData));
247 return cpsDeltaService.getDeltaReports(sourceDataNodesRebuilt, targetDataNodes);
251 @Timed(value = "cps.data.service.datanode.descendants.update",
252 description = "Time taken to update a data node and descendants")
253 public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
254 final String parentNodeXpath, final String jsonData,
255 final OffsetDateTime observedTimestamp) {
256 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
257 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
258 final Collection<DataNode> dataNodes =
259 buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON);
260 cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
261 sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp);
265 @Timed(value = "cps.data.service.datanode.descendants.batch.update",
266 description = "Time taken to update a batch of data nodes and descendants")
267 public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
268 final Map<String, String> nodesJsonData,
269 final OffsetDateTime observedTimestamp) {
270 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
271 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
272 final Collection<DataNode> dataNodes = buildDataNodesWithParentNodeXpath(anchor, nodesJsonData);
273 cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
274 nodesJsonData.keySet().forEach(nodeXpath ->
275 sendDataUpdatedEvent(anchor, nodeXpath, Operation.UPDATE, observedTimestamp));
279 @Timed(value = "cps.data.service.list.update",
280 description = "Time taken to update a list")
281 public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
282 final String jsonData, final OffsetDateTime observedTimestamp) {
283 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
284 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
285 final Collection<DataNode> newListElements =
286 buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON);
287 replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements, observedTimestamp);
291 @Timed(value = "cps.data.service.list.batch.update",
292 description = "Time taken to update a batch of lists")
293 public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
294 final Collection<DataNode> dataNodes, final OffsetDateTime observedTimestamp) {
295 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
296 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
297 cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, dataNodes);
298 sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp);
302 @Timed(value = "cps.data.service.datanode.delete",
303 description = "Time taken to delete a datanode")
304 public void deleteDataNode(final String dataspaceName, final String anchorName, final String dataNodeXpath,
305 final OffsetDateTime observedTimestamp) {
306 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
307 cpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, dataNodeXpath);
308 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
309 sendDataUpdatedEvent(anchor, dataNodeXpath, Operation.DELETE, observedTimestamp);
313 @Timed(value = "cps.data.service.datanode.batch.delete",
314 description = "Time taken to delete a batch of datanodes")
315 public void deleteDataNodes(final String dataspaceName, final String anchorName,
316 final Collection<String> dataNodeXpaths, final OffsetDateTime observedTimestamp) {
317 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
318 cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName, dataNodeXpaths);
319 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
320 dataNodeXpaths.forEach(dataNodeXpath ->
321 sendDataUpdatedEvent(anchor, dataNodeXpath, Operation.DELETE, observedTimestamp));
326 @Timed(value = "cps.data.service.datanode.delete.anchor",
327 description = "Time taken to delete all datanodes for an anchor")
328 public void deleteDataNodes(final String dataspaceName, final String anchorName,
329 final OffsetDateTime observedTimestamp) {
330 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
331 cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName);
332 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
333 sendDataUpdatedEvent(anchor, ROOT_NODE_XPATH, Operation.DELETE, observedTimestamp);
337 @Timed(value = "cps.data.service.datanode.delete.anchor.batch",
338 description = "Time taken to delete all datanodes for multiple anchors")
339 public void deleteDataNodes(final String dataspaceName, final Collection<String> anchorNames,
340 final OffsetDateTime observedTimestamp) {
341 cpsValidator.validateNameCharacters(dataspaceName);
342 cpsValidator.validateNameCharacters(anchorNames);
343 cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorNames);
344 for (final Anchor anchor : cpsAnchorService.getAnchors(dataspaceName, anchorNames)) {
345 sendDataUpdatedEvent(anchor, ROOT_NODE_XPATH, Operation.DELETE, observedTimestamp);
350 @Timed(value = "cps.data.service.list.delete",
351 description = "Time taken to delete a list or list element")
352 public void deleteListOrListElement(final String dataspaceName, final String anchorName, final String listNodeXpath,
353 final OffsetDateTime observedTimestamp) {
354 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
355 cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath);
356 final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
357 sendDataUpdatedEvent(anchor, listNodeXpath, Operation.DELETE, observedTimestamp);
361 private Collection<DataNode> rebuildSourceDataNodes(final String xpath, final Anchor sourceAnchor,
362 final Collection<DataNode> sourceDataNodes) {
364 final Collection<DataNode> sourceDataNodesRebuilt = new ArrayList<>();
365 if (sourceDataNodes != null) {
366 final String sourceDataNodesAsJson = getDataNodesAsJson(sourceAnchor, sourceDataNodes);
367 sourceDataNodesRebuilt.addAll(
368 buildDataNodesWithAnchorAndXpath(sourceAnchor, xpath, sourceDataNodesAsJson, ContentType.JSON));
370 return sourceDataNodesRebuilt;
373 private Collection<DataNode> buildTargetDataNodes(final Anchor sourceAnchor, final String xpath,
374 final Map<String, String> yangResourcesNameToContentMap,
375 final String targetData) {
376 if (yangResourcesNameToContentMap.isEmpty()) {
377 return buildDataNodesWithAnchorAndXpath(sourceAnchor, xpath, targetData, ContentType.JSON);
379 return buildDataNodesWithYangResourceAndXpath(yangResourcesNameToContentMap, xpath,
380 targetData, ContentType.JSON);
384 private String getDataNodesAsJson(final Anchor anchor, final Collection<DataNode> dataNodes) {
386 final List<Map<String, Object>> prefixToDataNodes = prefixResolver(anchor, dataNodes);
387 final Map<String, Object> targetDataAsJsonObject = getNodeDataAsJsonString(prefixToDataNodes);
388 return jsonObjectMapper.asJsonString(targetDataAsJsonObject);
391 private Map<String, Object> getNodeDataAsJsonString(final List<Map<String, Object>> prefixToDataNodes) {
392 final Map<String, Object> nodeDataAsJson = new HashMap<>();
393 for (final Map<String, Object> prefixToDataNode : prefixToDataNodes) {
394 nodeDataAsJson.putAll(prefixToDataNode);
396 return nodeDataAsJson;
399 private List<Map<String, Object>> prefixResolver(final Anchor anchor, final Collection<DataNode> dataNodes) {
400 final List<Map<String, Object>> prefixToDataNodes = new ArrayList<>(dataNodes.size());
401 for (final DataNode dataNode: dataNodes) {
402 final String prefix = prefixResolver
403 .getPrefix(anchor.getDataspaceName(), anchor.getName(), dataNode.getXpath());
404 final Map<String, Object> prefixToDataNode = DataMapUtils.toDataMapWithIdentifier(dataNode, prefix);
405 prefixToDataNodes.add(prefixToDataNode);
407 return prefixToDataNodes;
410 private Collection<DataNode> buildDataNodesWithParentNodeXpath(final Anchor anchor,
411 final Map<String, String> nodesJsonData) {
412 final Collection<DataNode> dataNodes = new ArrayList<>();
413 for (final Map.Entry<String, String> nodeJsonData : nodesJsonData.entrySet()) {
414 dataNodes.addAll(buildDataNodesWithParentNodeXpath(anchor, nodeJsonData.getKey(),
415 nodeJsonData.getValue(), ContentType.JSON));
420 private Collection<DataNode> buildDataNodesWithParentNodeXpath(final Anchor anchor, final String parentNodeXpath,
421 final String nodeData, final ContentType contentType) {
423 if (ROOT_NODE_XPATH.equals(parentNodeXpath)) {
424 final ContainerNode containerNode = yangParser.parseData(contentType, nodeData, anchor, "");
425 final Collection<DataNode> dataNodes = new DataNodeBuilder()
426 .withContainerNode(containerNode)
428 if (dataNodes.isEmpty()) {
429 throw new DataValidationException(NO_DATA_NODES, "No data nodes provided");
433 final String normalizedParentNodeXpath = CpsPathUtil.getNormalizedXpath(parentNodeXpath);
434 final ContainerNode containerNode =
435 yangParser.parseData(contentType, nodeData, anchor, normalizedParentNodeXpath);
436 final Collection<DataNode> dataNodes = new DataNodeBuilder()
437 .withParentNodeXpath(normalizedParentNodeXpath)
438 .withContainerNode(containerNode)
440 if (dataNodes.isEmpty()) {
441 throw new DataValidationException(NO_DATA_NODES, "No data nodes provided");
446 private Collection<DataNode> buildDataNodesWithParentNodeXpath(
447 final Map<String, String> yangResourcesNameToContentMap, final String xpath,
448 final String nodeData, final ContentType contentType) {
450 if (isRootNodeXpath(xpath)) {
451 final ContainerNode containerNode = yangParser.parseData(contentType, nodeData,
452 yangResourcesNameToContentMap, "");
453 final Collection<DataNode> dataNodes = new DataNodeBuilder()
454 .withContainerNode(containerNode)
456 if (dataNodes.isEmpty()) {
457 throw new DataValidationException(NO_DATA_NODES, "Data nodes were not found under the xpath " + xpath);
461 final String normalizedParentNodeXpath = CpsPathUtil.getNormalizedXpath(xpath);
462 final ContainerNode containerNode =
463 yangParser.parseData(contentType, nodeData, yangResourcesNameToContentMap, normalizedParentNodeXpath);
464 final Collection<DataNode> dataNodes = new DataNodeBuilder()
465 .withParentNodeXpath(normalizedParentNodeXpath)
466 .withContainerNode(containerNode)
468 if (dataNodes.isEmpty()) {
469 throw new DataValidationException(NO_DATA_NODES, "Data nodes were not found under the xpath " + xpath);
474 private Collection<DataNode> buildDataNodesWithAnchorAndXpath(final Anchor anchor, final String xpath,
475 final String nodeData,
476 final ContentType contentType) {
478 if (!isRootNodeXpath(xpath)) {
479 final String parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(xpath);
480 if (parentNodeXpath.isEmpty()) {
481 return buildDataNodesWithParentNodeXpath(anchor, ROOT_NODE_XPATH, nodeData, contentType);
483 return buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType);
485 return buildDataNodesWithParentNodeXpath(anchor, xpath, nodeData, contentType);
488 private Collection<DataNode> buildDataNodesWithYangResourceAndXpath(
489 final Map<String, String> yangResourcesNameToContentMap, final String xpath,
490 final String nodeData, final ContentType contentType) {
491 if (!isRootNodeXpath(xpath)) {
492 final String parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(xpath);
493 if (parentNodeXpath.isEmpty()) {
494 return buildDataNodesWithParentNodeXpath(yangResourcesNameToContentMap, ROOT_NODE_XPATH,
495 nodeData, contentType);
497 return buildDataNodesWithParentNodeXpath(yangResourcesNameToContentMap, parentNodeXpath,
498 nodeData, contentType);
500 return buildDataNodesWithParentNodeXpath(yangResourcesNameToContentMap, xpath, nodeData, contentType);
503 private static boolean isRootNodeXpath(final String xpath) {
504 return ROOT_NODE_XPATH.equals(xpath);
507 private void processDataNodeUpdate(final Anchor anchor, final DataNode dataNodeUpdate) {
508 cpsDataPersistenceService.batchUpdateDataLeaves(anchor.getDataspaceName(), anchor.getName(),
509 Collections.singletonMap(dataNodeUpdate.getXpath(), dataNodeUpdate.getLeaves()));
510 final Collection<DataNode> childDataNodeUpdates = dataNodeUpdate.getChildDataNodes();
511 for (final DataNode childDataNodeUpdate : childDataNodeUpdates) {
512 processDataNodeUpdate(anchor, childDataNodeUpdate);
516 private void sendDataUpdatedEvent(final Anchor anchor, final String xpath,
517 final Operation operation, final OffsetDateTime observedTimestamp) {
519 cpsDataUpdateEventsService.publishCpsDataUpdateEvent(anchor, xpath, operation, observedTimestamp);
520 } catch (final Exception exception) {
521 log.error("Failed to send message to notification service", exception);