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 void updateNodeLeaves(final String dataspaceName, final String anchorName, final String parentNodeXpath,
134 final String jsonData, final OffsetDateTime observedTimestamp) {
135 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
136 final DataNode dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData, ContentType.JSON);
137 cpsDataPersistenceService
138 .updateDataLeaves(dataspaceName, anchorName, dataNode.getXpath(), dataNode.getLeaves());
139 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
143 public void updateNodeLeavesAndExistingDescendantLeaves(final String dataspaceName, final String anchorName,
144 final String parentNodeXpath,
145 final String dataNodeUpdatesAsJson,
146 final OffsetDateTime observedTimestamp) {
147 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
148 final Collection<DataNode> dataNodeUpdates =
149 buildDataNodes(dataspaceName, anchorName,
150 parentNodeXpath, dataNodeUpdatesAsJson, ContentType.JSON);
151 for (final DataNode dataNodeUpdate : dataNodeUpdates) {
152 processDataNodeUpdate(dataspaceName, anchorName, dataNodeUpdate);
154 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
158 public String startSession() {
159 return cpsDataPersistenceService.startSession();
163 public void closeSession(final String sessionId) {
164 cpsDataPersistenceService.closeSession(sessionId);
168 public void lockAnchor(final String sessionID, final String dataspaceName, final String anchorName) {
169 lockAnchor(sessionID, dataspaceName, anchorName, DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS);
173 public void lockAnchor(final String sessionID, final String dataspaceName,
174 final String anchorName, final Long timeoutInMilliseconds) {
175 cpsDataPersistenceService.lockAnchor(sessionID, dataspaceName, anchorName, timeoutInMilliseconds);
179 public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
180 final String parentNodeXpath, final String jsonData,
181 final OffsetDateTime observedTimestamp) {
182 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
183 final Collection<DataNode> dataNodes =
184 buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData, ContentType.JSON);
185 final ArrayList<DataNode> nodes = new ArrayList<>(dataNodes);
186 cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, nodes);
187 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
191 public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
192 final Map<String, String> nodesJsonData,
193 final OffsetDateTime observedTimestamp) {
194 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
195 final List<DataNode> dataNodes = buildDataNodes(dataspaceName, anchorName, nodesJsonData);
196 cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
197 nodesJsonData.keySet().forEach(nodeXpath ->
198 processDataUpdatedEventAsync(dataspaceName, anchorName, nodeXpath,
199 UPDATE, observedTimestamp));
203 public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
204 final String jsonData, final OffsetDateTime observedTimestamp) {
205 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
206 final Collection<DataNode> newListElements =
207 buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData, ContentType.JSON);
208 replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements, observedTimestamp);
212 public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
213 final Collection<DataNode> dataNodes, final OffsetDateTime observedTimestamp) {
214 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
215 cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, dataNodes);
216 processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
220 public void deleteDataNode(final String dataspaceName, final String anchorName, final String dataNodeXpath,
221 final OffsetDateTime observedTimestamp) {
222 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
223 cpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, dataNodeXpath);
224 processDataUpdatedEventAsync(dataspaceName, anchorName, dataNodeXpath, DELETE, observedTimestamp);
228 public void deleteDataNodes(final String dataspaceName, final String anchorName,
229 final OffsetDateTime observedTimestamp) {
230 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
231 processDataUpdatedEventAsync(dataspaceName, anchorName, ROOT_NODE_XPATH, DELETE, observedTimestamp);
232 cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName);
236 public void deleteListOrListElement(final String dataspaceName, final String anchorName, final String listNodeXpath,
237 final OffsetDateTime observedTimestamp) {
238 cpsValidator.validateNameCharacters(dataspaceName, anchorName);
239 cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath);
240 processDataUpdatedEventAsync(dataspaceName, anchorName, listNodeXpath, DELETE, observedTimestamp);
243 private DataNode buildDataNode(final String dataspaceName, final String anchorName,
244 final String parentNodeXpath, final String nodeData,
245 final ContentType contentType) {
247 final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
248 final SchemaContext schemaContext = getSchemaContext(dataspaceName, anchor.getSchemaSetName());
250 if (ROOT_NODE_XPATH.equals(parentNodeXpath)) {
251 final ContainerNode containerNode = YangUtils.parseData(contentType, nodeData, schemaContext);
252 return new DataNodeBuilder().withContainerNode(containerNode).build();
255 final ContainerNode containerNode = YangUtils
256 .parseData(contentType, nodeData, schemaContext, parentNodeXpath);
258 return new DataNodeBuilder()
259 .withParentNodeXpath(parentNodeXpath)
260 .withContainerNode(containerNode)
264 private List<DataNode> buildDataNodes(final String dataspaceName, final String anchorName,
265 final Map<String, String> nodesJsonData) {
266 return nodesJsonData.entrySet().stream().map(nodeJsonData ->
267 buildDataNode(dataspaceName, anchorName, nodeJsonData.getKey(),
268 nodeJsonData.getValue(), ContentType.JSON)).collect(Collectors.toList());
271 private Collection<DataNode> buildDataNodes(final String dataspaceName,
272 final String anchorName,
273 final String parentNodeXpath,
274 final String nodeData,
275 final ContentType contentType) {
277 final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
278 final SchemaContext schemaContext = getSchemaContext(dataspaceName, anchor.getSchemaSetName());
279 if (ROOT_NODE_XPATH.equals(parentNodeXpath)) {
280 final ContainerNode containerNode = YangUtils.parseData(contentType, nodeData, schemaContext);
281 final Collection<DataNode> dataNodes = new DataNodeBuilder()
282 .withContainerNode(containerNode)
284 if (dataNodes.isEmpty()) {
285 throw new DataValidationException("Invalid data.", "No data nodes provided");
289 final ContainerNode containerNode = YangUtils.parseData(contentType, nodeData, schemaContext, parentNodeXpath);
290 final Collection<DataNode> dataNodes = new DataNodeBuilder()
291 .withParentNodeXpath(parentNodeXpath)
292 .withContainerNode(containerNode)
294 if (dataNodes.isEmpty()) {
295 throw new DataValidationException("Invalid data.", "No data nodes provided");
301 private Collection<Collection<DataNode>> buildDataNodes(final String dataspaceName, final String anchorName,
302 final String parentNodeXpath, final Collection<String> nodeDataList, final ContentType contentType) {
303 return nodeDataList.stream()
304 .map(nodeData -> buildDataNodes(dataspaceName, anchorName, parentNodeXpath, nodeData, contentType))
305 .collect(Collectors.toList());
308 private void processDataUpdatedEventAsync(final String dataspaceName, final String anchorName, final String xpath,
309 final Operation operation, final OffsetDateTime observedTimestamp) {
311 notificationService.processDataUpdatedEvent(dataspaceName, anchorName, xpath, operation, observedTimestamp);
312 } catch (final Exception exception) {
313 //If async message can't be queued for notification service, the initial request should not failed.
314 log.error("Failed to send message to notification service", exception);
318 private SchemaContext getSchemaContext(final String dataspaceName, final String schemaSetName) {
319 return yangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName).getSchemaContext();
322 private void processDataNodeUpdate(final String dataspaceName, final String anchorName,
323 final DataNode dataNodeUpdate) {
324 if (dataNodeUpdate == null) {
327 cpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, dataNodeUpdate.getXpath(),
328 dataNodeUpdate.getLeaves());
329 final Collection<DataNode> childDataNodeUpdates = dataNodeUpdate.getChildDataNodes();
330 for (final DataNode childDataNodeUpdate : childDataNodeUpdates) {
331 processDataNodeUpdate(dataspaceName, anchorName, childDataNodeUpdate);