2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2022 Nordix Foundation
4 * Modifications Copyright (C) 2020-2022 Bell Canada.
5 * Modifications Copyright (C) 2021 Pantheon.tech
6 * Modifications Copyright (C) 2022 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 static org.onap.cps.notification.Operation.CREATE;
28 import static org.onap.cps.notification.Operation.DELETE;
29 import static org.onap.cps.notification.Operation.UPDATE;
31 import java.time.OffsetDateTime;
32 import java.util.ArrayList;
33 import java.util.Collection;
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.CpsAdminService;
40 import org.onap.cps.api.CpsDataService;
41 import org.onap.cps.notification.NotificationService;
42 import org.onap.cps.notification.Operation;
43 import org.onap.cps.spi.CpsDataPersistenceService;
44 import org.onap.cps.spi.FetchDescendantsOption;
45 import org.onap.cps.spi.exceptions.DataValidationException;
46 import org.onap.cps.spi.model.Anchor;
47 import org.onap.cps.spi.model.DataNode;
48 import org.onap.cps.spi.model.DataNodeBuilder;
49 import org.onap.cps.spi.utils.CpsValidator;
50 import org.onap.cps.utils.ContentType;
51 import org.onap.cps.utils.YangUtils;
52 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
53 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
54 import org.springframework.stereotype.Service;
58 @RequiredArgsConstructor
59 public class CpsDataServiceImpl implements CpsDataService {
61 private static final String ROOT_NODE_XPATH = "/";
62 private static final long DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS = 300L;
64 private final CpsDataPersistenceService cpsDataPersistenceService;
65 private final CpsAdminService cpsAdminService;
66 private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache;
67 private final NotificationService notificationService;
68 private final CpsValidator cpsValidator;
71 public void saveData(final String dataspaceName, final String anchorName, final String nodeData,
72 final OffsetDateTime observedTimestamp) {
73 saveData(dataspaceName, anchorName, nodeData, observedTimestamp, ContentType.JSON);
77 public void saveData(final String dataspaceName, final String anchorName, final String nodeData,
78 final OffsetDateTime observedTimestamp, final ContentType contentType) {
79 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
80 final Collection<DataNode> dataNodes =
81 buildDataNodes(dataspaceName, anchorName, ROOT_NODE_XPATH, nodeData, contentType);
82 cpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, dataNodes);
83 processDataUpdatedEventAsync(dataspaceName, anchorName, ROOT_NODE_XPATH, CREATE, observedTimestamp);
87 public void saveData(final String dataspaceName, final String anchorName, final String parentNodeXpath,
88 final String nodeData, final OffsetDateTime observedTimestamp) {
89 saveData(dataspaceName, anchorName, parentNodeXpath, nodeData, observedTimestamp, ContentType.JSON);
93 public void saveData(final String dataspaceName, final String anchorName, final String parentNodeXpath,
94 final String nodeData, final OffsetDateTime observedTimestamp,
95 final ContentType contentType) {
96 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
97 final Collection<DataNode> dataNodes =
98 buildDataNodes(dataspaceName, anchorName, parentNodeXpath, nodeData, contentType);
99 cpsDataPersistenceService.addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes);
100 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, CREATE, observedTimestamp);
104 public void saveListElements(final String dataspaceName, final String anchorName,
105 final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) {
106 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
107 final Collection<DataNode> listElementDataNodeCollection =
108 buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData, ContentType.JSON);
109 cpsDataPersistenceService.addListElements(dataspaceName, anchorName, parentNodeXpath,
110 listElementDataNodeCollection);
111 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
115 public void saveListElementsBatch(final String dataspaceName, final String anchorName, final String parentNodeXpath,
116 final Collection<String> jsonDataList, final OffsetDateTime observedTimestamp) {
117 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
118 final Collection<Collection<DataNode>> listElementDataNodeCollections =
119 buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonDataList, ContentType.JSON);
120 cpsDataPersistenceService.addMultipleLists(dataspaceName, anchorName, parentNodeXpath,
121 listElementDataNodeCollections);
122 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
126 public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath,
127 final FetchDescendantsOption fetchDescendantsOption) {
128 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
129 return cpsDataPersistenceService.getDataNode(dataspaceName, anchorName, xpath, fetchDescendantsOption);
133 public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
134 final Collection<String> xpaths,
135 final FetchDescendantsOption fetchDescendantsOption) {
136 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
137 return cpsDataPersistenceService.getDataNodes(dataspaceName, anchorName, xpaths, fetchDescendantsOption);
141 public void updateNodeLeaves(final String dataspaceName, final String anchorName, final String parentNodeXpath,
142 final String jsonData, final OffsetDateTime observedTimestamp) {
143 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
144 final DataNode dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData, ContentType.JSON);
145 cpsDataPersistenceService
146 .updateDataLeaves(dataspaceName, anchorName, dataNode.getXpath(), dataNode.getLeaves());
147 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
151 public void updateNodeLeavesAndExistingDescendantLeaves(final String dataspaceName, final String anchorName,
152 final String parentNodeXpath,
153 final String dataNodeUpdatesAsJson,
154 final OffsetDateTime observedTimestamp) {
155 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
156 final Collection<DataNode> dataNodeUpdates =
157 buildDataNodes(dataspaceName, anchorName,
158 parentNodeXpath, dataNodeUpdatesAsJson, ContentType.JSON);
159 for (final DataNode dataNodeUpdate : dataNodeUpdates) {
160 processDataNodeUpdate(dataspaceName, anchorName, dataNodeUpdate);
162 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
166 public String startSession() {
167 return cpsDataPersistenceService.startSession();
171 public void closeSession(final String sessionId) {
172 cpsDataPersistenceService.closeSession(sessionId);
176 public void lockAnchor(final String sessionID, final String dataspaceName, final String anchorName) {
177 lockAnchor(sessionID, dataspaceName, anchorName, DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS);
181 public void lockAnchor(final String sessionID, final String dataspaceName,
182 final String anchorName, final Long timeoutInMilliseconds) {
183 cpsDataPersistenceService.lockAnchor(sessionID, dataspaceName, anchorName, timeoutInMilliseconds);
187 public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
188 final String parentNodeXpath, final String jsonData,
189 final OffsetDateTime observedTimestamp) {
190 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
191 final Collection<DataNode> dataNodes =
192 buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData, ContentType.JSON);
193 final ArrayList<DataNode> nodes = new ArrayList<>(dataNodes);
194 cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, nodes);
195 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
199 public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
200 final Map<String, String> nodesJsonData,
201 final OffsetDateTime observedTimestamp) {
202 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
203 final List<DataNode> dataNodes = buildDataNodes(dataspaceName, anchorName, nodesJsonData);
204 cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
205 nodesJsonData.keySet().forEach(nodeXpath ->
206 processDataUpdatedEventAsync(dataspaceName, anchorName, nodeXpath,
207 UPDATE, observedTimestamp));
211 public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
212 final String jsonData, final OffsetDateTime observedTimestamp) {
213 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
214 final Collection<DataNode> newListElements =
215 buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData, ContentType.JSON);
216 replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements, observedTimestamp);
220 public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
221 final Collection<DataNode> dataNodes, final OffsetDateTime observedTimestamp) {
222 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
223 cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, dataNodes);
224 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
228 public void deleteDataNode(final String dataspaceName, final String anchorName, final String dataNodeXpath,
229 final OffsetDateTime observedTimestamp) {
230 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
231 cpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, dataNodeXpath);
232 processDataUpdatedEventAsync(dataspaceName, anchorName, dataNodeXpath, DELETE, observedTimestamp);
236 public void deleteDataNodes(final String dataspaceName, final String anchorName,
237 final OffsetDateTime observedTimestamp) {
238 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
239 processDataUpdatedEventAsync(dataspaceName, anchorName, ROOT_NODE_XPATH, DELETE, observedTimestamp);
240 cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName);
244 public void deleteListOrListElement(final String dataspaceName, final String anchorName, final String listNodeXpath,
245 final OffsetDateTime observedTimestamp) {
246 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
247 cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath);
248 processDataUpdatedEventAsync(dataspaceName, anchorName, listNodeXpath, DELETE, observedTimestamp);
251 private DataNode buildDataNode(final String dataspaceName, final String anchorName,
252 final String parentNodeXpath, final String nodeData,
253 final ContentType contentType) {
255 final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
256 final SchemaContext schemaContext = getSchemaContext(dataspaceName, anchor.getSchemaSetName());
258 if (ROOT_NODE_XPATH.equals(parentNodeXpath)) {
259 final ContainerNode containerNode = YangUtils.parseData(contentType, nodeData, schemaContext);
260 return new DataNodeBuilder().withContainerNode(containerNode).build();
263 final ContainerNode containerNode = YangUtils.parseData(contentType, nodeData, schemaContext, parentNodeXpath);
265 return new DataNodeBuilder()
266 .withParentNodeXpath(parentNodeXpath)
267 .withContainerNode(containerNode)
271 private List<DataNode> buildDataNodes(final String dataspaceName, final String anchorName,
272 final Map<String, String> nodesJsonData) {
273 return nodesJsonData.entrySet().stream().map(nodeJsonData ->
274 buildDataNode(dataspaceName, anchorName, nodeJsonData.getKey(),
275 nodeJsonData.getValue(), ContentType.JSON)).collect(Collectors.toList());
278 private Collection<DataNode> buildDataNodes(final String dataspaceName,
279 final String anchorName,
280 final String parentNodeXpath,
281 final String nodeData,
282 final ContentType contentType) {
284 final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
285 final SchemaContext schemaContext = getSchemaContext(dataspaceName, anchor.getSchemaSetName());
286 if (ROOT_NODE_XPATH.equals(parentNodeXpath)) {
287 final ContainerNode containerNode = YangUtils.parseData(contentType, nodeData, schemaContext);
288 final Collection<DataNode> dataNodes = new DataNodeBuilder()
289 .withContainerNode(containerNode)
291 if (dataNodes.isEmpty()) {
292 throw new DataValidationException("Invalid data.", "No data nodes provided");
296 final ContainerNode containerNode = YangUtils.parseData(contentType, nodeData, schemaContext, parentNodeXpath);
297 final Collection<DataNode> dataNodes = new DataNodeBuilder()
298 .withParentNodeXpath(parentNodeXpath)
299 .withContainerNode(containerNode)
301 if (dataNodes.isEmpty()) {
302 throw new DataValidationException("Invalid data.", "No data nodes provided");
308 private Collection<Collection<DataNode>> buildDataNodes(final String dataspaceName, final String anchorName,
309 final String parentNodeXpath, final Collection<String> nodeDataList, final ContentType contentType) {
310 return nodeDataList.stream()
311 .map(nodeData -> buildDataNodes(dataspaceName, anchorName, parentNodeXpath, nodeData, contentType))
312 .collect(Collectors.toList());
315 private void processDataUpdatedEventAsync(final String dataspaceName, final String anchorName, final String xpath,
316 final Operation operation, final OffsetDateTime observedTimestamp) {
318 notificationService.processDataUpdatedEvent(dataspaceName, anchorName, xpath, operation, observedTimestamp);
319 } catch (final Exception exception) {
320 //If async message can't be queued for notification service, the initial request should not failed.
321 log.error("Failed to send message to notification service", exception);
325 private SchemaContext getSchemaContext(final String dataspaceName, final String schemaSetName) {
326 return yangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName).getSchemaContext();
329 private void processDataNodeUpdate(final String dataspaceName, final String anchorName,
330 final DataNode dataNodeUpdate) {
331 if (dataNodeUpdate == null) {
334 cpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, dataNodeUpdate.getXpath(),
335 dataNodeUpdate.getLeaves());
336 final Collection<DataNode> childDataNodeUpdates = dataNodeUpdate.getChildDataNodes();
337 for (final DataNode childDataNodeUpdate : childDataNodeUpdates) {
338 processDataNodeUpdate(dataspaceName, anchorName, childDataNodeUpdate);