2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.ncmp.api.impl.subscriptions;
23 import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
25 import java.io.Serializable;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.List;
31 import java.util.Optional;
32 import java.util.stream.Collectors;
33 import lombok.RequiredArgsConstructor;
34 import lombok.extern.slf4j.Slf4j;
35 import org.onap.cps.api.CpsDataService;
36 import org.onap.cps.ncmp.api.impl.utils.DataNodeHelper;
37 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelSubscriptionEvent;
38 import org.onap.cps.spi.FetchDescendantsOption;
39 import org.onap.cps.spi.model.DataNode;
40 import org.onap.cps.utils.JsonObjectMapper;
41 import org.springframework.stereotype.Component;
44 @RequiredArgsConstructor
46 public class SubscriptionPersistenceImpl implements SubscriptionPersistence {
48 private static final String SUBSCRIPTION_DATASPACE_NAME = "NCMP-Admin";
49 private static final String SUBSCRIPTION_ANCHOR_NAME = "AVC-Subscriptions";
50 private static final String SUBSCRIPTION_REGISTRY_PARENT = "/subscription-registry";
51 private final JsonObjectMapper jsonObjectMapper;
52 private final CpsDataService cpsDataService;
55 public void saveSubscriptionEvent(final YangModelSubscriptionEvent yangModelSubscriptionEvent) {
56 final String clientId = yangModelSubscriptionEvent.getClientId();
57 final String subscriptionName = yangModelSubscriptionEvent.getSubscriptionName();
59 final Collection<DataNode> dataNodes = cpsDataService.getDataNodes(SUBSCRIPTION_DATASPACE_NAME,
60 SUBSCRIPTION_ANCHOR_NAME, SUBSCRIPTION_REGISTRY_PARENT, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
62 if (isSubscriptionRegistryEmptyOrNonExist(dataNodes, clientId, subscriptionName)) {
63 saveSubscriptionEventYangModel(createSubscriptionEventJsonData(
64 jsonObjectMapper.asJsonString(yangModelSubscriptionEvent)));
66 findDeltaCmHandlesAddOrUpdateInDatabase(yangModelSubscriptionEvent, clientId, subscriptionName, dataNodes);
70 private void findDeltaCmHandlesAddOrUpdateInDatabase(final YangModelSubscriptionEvent yangModelSubscriptionEvent,
71 final String clientId, final String subscriptionName,
72 final Collection<DataNode> dataNodes) {
73 final Map<String, SubscriptionStatus> cmHandleIdsFromYangModel =
74 extractCmHandleFromYangModelAsMap(yangModelSubscriptionEvent);
75 final Map<String, SubscriptionStatus> cmHandleIdsFromDatabase =
76 extractCmHandleFromDbAsMap(dataNodes);
78 final Map<String, SubscriptionStatus> newCmHandles =
79 mapDifference(cmHandleIdsFromYangModel, cmHandleIdsFromDatabase);
80 traverseCmHandleList(newCmHandles, clientId, subscriptionName, true);
82 final Map<String, SubscriptionStatus> existingCmHandles =
83 mapDifference(cmHandleIdsFromYangModel, newCmHandles);
84 traverseCmHandleList(existingCmHandles, clientId, subscriptionName, false);
87 private boolean isSubscriptionRegistryEmptyOrNonExist(final Collection<DataNode> dataNodes,
88 final String clientId, final String subscriptionName) {
89 final Optional<DataNode> dataNodeFirst = dataNodes.stream().findFirst();
90 return ((dataNodeFirst.isPresent() && dataNodeFirst.get().getChildDataNodes().isEmpty())
91 || getCmHandlesForSubscriptionEvent(clientId, subscriptionName).isEmpty());
94 private void traverseCmHandleList(final Map<String, SubscriptionStatus> cmHandleMap,
95 final String clientId,
96 final String subscriptionName,
97 final boolean isAddListElementOperation) {
98 final List<YangModelSubscriptionEvent.TargetCmHandle> cmHandleList =
99 targetCmHandlesAsList(cmHandleMap);
100 for (final YangModelSubscriptionEvent.TargetCmHandle targetCmHandle : cmHandleList) {
101 final String targetCmHandleAsJson =
102 createTargetCmHandleJsonData(jsonObjectMapper.asJsonString(targetCmHandle));
103 addOrReplaceCmHandlePredicateListElement(targetCmHandleAsJson, clientId, subscriptionName,
104 isAddListElementOperation);
108 private void addOrReplaceCmHandlePredicateListElement(final String targetCmHandleAsJson,
109 final String clientId,
110 final String subscriptionName,
111 final boolean isAddListElementOperation) {
112 if (isAddListElementOperation) {
113 log.info("targetCmHandleAsJson to be added into DB {}", targetCmHandleAsJson);
114 cpsDataService.saveListElements(SUBSCRIPTION_DATASPACE_NAME,
115 SUBSCRIPTION_ANCHOR_NAME, createCmHandleXpathPredicates(clientId, subscriptionName),
116 targetCmHandleAsJson, NO_TIMESTAMP);
118 log.info("targetCmHandleAsJson to be updated into DB {}", targetCmHandleAsJson);
119 cpsDataService.updateNodeLeaves(SUBSCRIPTION_DATASPACE_NAME,
120 SUBSCRIPTION_ANCHOR_NAME, createCmHandleXpathPredicates(clientId, subscriptionName),
121 targetCmHandleAsJson, NO_TIMESTAMP);
125 private void saveSubscriptionEventYangModel(final String subscriptionEventJsonData) {
126 log.info("SubscriptionEventJsonData to be saved into DB {}", subscriptionEventJsonData);
127 cpsDataService.saveListElements(SUBSCRIPTION_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
128 SUBSCRIPTION_REGISTRY_PARENT, subscriptionEventJsonData, NO_TIMESTAMP);
132 public Collection<DataNode> getDataNodesForSubscriptionEvent() {
133 return cpsDataService.getDataNodes(SUBSCRIPTION_DATASPACE_NAME,
134 SUBSCRIPTION_ANCHOR_NAME, SUBSCRIPTION_REGISTRY_PARENT,
135 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
139 public Collection<DataNode> getCmHandlesForSubscriptionEvent(final String clientId, final String subscriptionName) {
140 return cpsDataService.getDataNodesForMultipleXpaths(SUBSCRIPTION_DATASPACE_NAME,
141 SUBSCRIPTION_ANCHOR_NAME, Arrays.asList(createCmHandleXpath(clientId, subscriptionName)),
142 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
145 private static Map<String, SubscriptionStatus> extractCmHandleFromDbAsMap(final Collection<DataNode> dataNodes) {
146 final List<Map<String, Serializable>> dataNodeLeaves = DataNodeHelper.getDataNodeLeaves(dataNodes);
147 final List<Collection<Serializable>> cmHandleIdToStatus = DataNodeHelper.getCmHandleIdToStatus(dataNodeLeaves);
148 return DataNodeHelper.getCmHandleIdToStatusMap(cmHandleIdToStatus);
151 private static Map<String, SubscriptionStatus> extractCmHandleFromYangModelAsMap(
152 final YangModelSubscriptionEvent yangModelSubscriptionEvent) {
153 return yangModelSubscriptionEvent.getPredicates().getTargetCmHandles()
154 .stream().collect(Collectors.toMap(
155 YangModelSubscriptionEvent.TargetCmHandle::getCmHandleId,
156 YangModelSubscriptionEvent.TargetCmHandle::getStatus));
159 private static List<YangModelSubscriptionEvent.TargetCmHandle> targetCmHandlesAsList(
160 final Map<String, SubscriptionStatus> newCmHandles) {
161 return newCmHandles.entrySet().stream().map(entry ->
162 new YangModelSubscriptionEvent.TargetCmHandle(entry.getKey(),
163 entry.getValue())).collect(Collectors.toList());
166 private static String createSubscriptionEventJsonData(final String yangModelSubscriptionAsJson) {
167 return "{\"subscription\":[" + yangModelSubscriptionAsJson + "]}";
170 private static String createTargetCmHandleJsonData(final String targetCmHandleAsJson) {
171 return "{\"targetCmHandles\":[" + targetCmHandleAsJson + "]}";
174 private static String createCmHandleXpathPredicates(final String clientId, final String subscriptionName) {
175 return "/subscription-registry/subscription[@clientID='" + clientId
176 + "' and @subscriptionName='" + subscriptionName + "']/predicates";
179 private static String createCmHandleXpath(final String clientId, final String subscriptionName) {
180 return "/subscription-registry/subscription[@clientID='" + clientId
181 + "' and @subscriptionName='" + subscriptionName + "']";
184 private static <K, V> Map<K, V> mapDifference(final Map<? extends K, ? extends V> left,
185 final Map<? extends K, ? extends V> right) {
186 final Map<K, V> difference = new HashMap<>();
187 difference.putAll(left);
188 difference.putAll(right);
189 difference.entrySet().removeAll(right.entrySet());