2f61b228b3a101ec8c3df902d5dc786ab46f3008
[cps.git] / cps-ncmp-service / src / main / java / org / onap / cps / ncmp / api / impl / NetworkCmProxyDataServicePropertyHandler.java
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2022-2023 Nordix Foundation
4  *  Modifications Copyright (C) 2022 Bell Canada
5  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
6  *  ================================================================================
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *  Unless required by applicable law or agreed to in writing, software
14  *  distributed under the License is distributed on an "AS IS" BASIS,
15  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *  See the License for the specific language governing permissions and
17  *  limitations under the License.
18  *
19  *  SPDX-License-Identifier: Apache-2.0
20  *  ============LICENSE_END=========================================================
21  */
22
23 package org.onap.cps.ncmp.api.impl;
24
25 import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY;
26 import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY;
27
28 import com.google.common.collect.ImmutableMap;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.HashSet;
32 import java.util.LinkedHashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
37 import lombok.RequiredArgsConstructor;
38 import lombok.extern.slf4j.Slf4j;
39 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
40 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
41 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError;
42 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
43 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
44 import org.onap.cps.spi.exceptions.DataValidationException;
45 import org.onap.cps.spi.model.DataNode;
46 import org.onap.cps.spi.model.DataNodeBuilder;
47 import org.springframework.stereotype.Service;
48
49 @Slf4j
50 @Service
51 @RequiredArgsConstructor
52 //Accepting the security hotspot as the string checked is generated from inside code and not user input.
53 @SuppressWarnings("squid:S5852")
54 public class NetworkCmProxyDataServicePropertyHandler {
55
56     private final InventoryPersistence inventoryPersistence;
57
58     /**
59      * Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes.
60      * The attributes which are not passed will remain as is.
61      *
62      * @param ncmpServiceCmHandles collection of ncmpServiceCmHandles
63      */
64     public List<CmHandleRegistrationResponse> updateCmHandleProperties(
65         final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
66         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
67         for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
68             final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
69             try {
70                 final DataNode existingCmHandleDataNode = inventoryPersistence.getCmHandleDataNode(cmHandleId)
71                         .iterator().next();
72                 processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle);
73                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId));
74             } catch (final DataNodeNotFoundException e) {
75                 log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
76                     cmHandleId, e.getMessage());
77                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
78                     .createFailureResponse(cmHandleId, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
79             } catch (final DataValidationException e) {
80                 log.error("Unable to update cm handle : {}, caused by : {}",
81                     cmHandleId, e.getMessage());
82                 cmHandleRegistrationResponses.add(
83                     CmHandleRegistrationResponse.createFailureResponse(cmHandleId,
84                         RegistrationError.CM_HANDLE_INVALID_ID));
85             } catch (final Exception exception) {
86                 log.error("Unable to update cmHandle : {} , caused by : {}",
87                     cmHandleId, exception.getMessage());
88                 cmHandleRegistrationResponses.add(
89                     CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception));
90             }
91         }
92         return cmHandleRegistrationResponses;
93     }
94
95     private void processUpdates(final DataNode existingCmHandleDataNode, final NcmpServiceCmHandle incomingCmHandle) {
96         if (!incomingCmHandle.getPublicProperties().isEmpty()) {
97             updateProperties(existingCmHandleDataNode, PUBLIC_PROPERTY, incomingCmHandle.getPublicProperties());
98         }
99         if (!incomingCmHandle.getDmiProperties().isEmpty()) {
100             updateProperties(existingCmHandleDataNode, DMI_PROPERTY, incomingCmHandle.getDmiProperties());
101         }
102     }
103
104     private void updateProperties(final DataNode existingCmHandleDataNode, final PropertyType propertyType,
105             final Map<String, String> incomingProperties) {
106         final Collection<DataNode> replacementPropertyDataNodes =
107                 getReplacementDataNodes(existingCmHandleDataNode, propertyType, incomingProperties);
108         replacementPropertyDataNodes.addAll(
109                 getUnchangedPropertyDataNodes(existingCmHandleDataNode, propertyType, incomingProperties));
110         if (replacementPropertyDataNodes.isEmpty()) {
111             removeAllProperties(existingCmHandleDataNode, propertyType);
112         } else {
113             inventoryPersistence.replaceListContent(existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes);
114         }
115     }
116
117     private void removeAllProperties(final DataNode existingCmHandleDataNode, final PropertyType propertyType) {
118         existingCmHandleDataNode.getChildDataNodes().forEach(dataNode -> {
119             final Matcher matcher = propertyType.propertyXpathPattern.matcher(dataNode.getXpath());
120             if (matcher.find()) {
121                 log.info("Deleting dataNode with xpath : [{}]", dataNode.getXpath());
122                 inventoryPersistence.deleteDataNode(dataNode.getXpath());
123             }
124         });
125     }
126
127     private Collection<DataNode> getUnchangedPropertyDataNodes(final DataNode existingCmHandleDataNode,
128             final PropertyType propertyType, final Map<String, String> incomingProperties) {
129         final Collection<DataNode> unchangedPropertyDataNodes = new HashSet<>();
130         for (final DataNode existingPropertyDataNode : existingCmHandleDataNode.getChildDataNodes()) {
131             final Matcher matcher = propertyType.propertyXpathPattern.matcher(existingPropertyDataNode.getXpath());
132             if (matcher.find()) {
133                 final String keyName = matcher.group(2);
134                 if (!incomingProperties.containsKey(keyName)) {
135                     unchangedPropertyDataNodes.add(existingPropertyDataNode);
136                 }
137             }
138         }
139         return unchangedPropertyDataNodes;
140     }
141
142     private Collection<DataNode> getReplacementDataNodes(final DataNode existingCmHandleDataNode,
143             final PropertyType propertyType, final Map<String, String> incomingProperties) {
144         final Collection<DataNode> replacementPropertyDataNodes = new HashSet<>();
145         incomingProperties.forEach((updatedAttributeKey, updatedAttributeValue) -> {
146             final String propertyXpath = getAttributeXpath(existingCmHandleDataNode, propertyType, updatedAttributeKey);
147             if (updatedAttributeValue != null) {
148                 log.info("Creating a new DataNode with xpath {} , key : {} and value : {}", propertyXpath,
149                         updatedAttributeKey, updatedAttributeValue);
150                 replacementPropertyDataNodes.add(
151                         buildDataNode(propertyXpath, updatedAttributeKey, updatedAttributeValue));
152             }
153         });
154         return replacementPropertyDataNodes;
155     }
156
157     private String getAttributeXpath(final DataNode cmHandle, final PropertyType propertyType,
158             final String attributeKey) {
159         return cmHandle.getXpath() + "/" + propertyType.xpathPrefix + String.format("[@name='%s']", attributeKey);
160     }
161
162     private DataNode buildDataNode(final String xpath, final String attributeKey, final String attributeValue) {
163         final Map<String, String> updatedLeaves = new LinkedHashMap<>(1);
164         updatedLeaves.put("name", attributeKey);
165         updatedLeaves.put("value", attributeValue);
166         log.debug("Building a new node with xpath {} with leaves (name : {} , value : {})", xpath, attributeKey,
167                 attributeValue);
168         return new DataNodeBuilder().withXpath(xpath).withLeaves(ImmutableMap.copyOf(updatedLeaves)).build();
169     }
170
171     enum PropertyType {
172         DMI_PROPERTY("additional-properties"), PUBLIC_PROPERTY("public-properties");
173
174         private static final String LIST_INDEX_PATTERN = "\\[@(\\w+)[^\\/]'([^']+)']";
175
176         final String xpathPrefix;
177         final Pattern propertyXpathPattern;
178
179         PropertyType(final String xpathPrefix) {
180             this.xpathPrefix = xpathPrefix;
181             this.propertyXpathPattern = Pattern.compile(xpathPrefix + LIST_INDEX_PATTERN);
182         }
183     }
184 }