2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023-2024 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.utils.data.operation;
23 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND;
24 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY;
26 import io.cloudevents.CloudEvent;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.List;
34 import java.util.concurrent.TimeoutException;
35 import java.util.stream.Collectors;
36 import lombok.AccessLevel;
37 import lombok.NoArgsConstructor;
38 import lombok.extern.slf4j.Slf4j;
39 import org.onap.cps.events.EventsPublisher;
40 import org.onap.cps.ncmp.api.NcmpResponseStatus;
41 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState;
42 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation;
43 import org.onap.cps.ncmp.api.impl.operations.DmiOperationCmHandle;
44 import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer;
45 import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext;
46 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
47 import org.onap.cps.ncmp.api.models.DataOperationDefinition;
48 import org.onap.cps.ncmp.api.models.DataOperationRequest;
49 import org.springframework.util.LinkedMultiValueMap;
50 import org.springframework.util.MultiValueMap;
53 @NoArgsConstructor(access = AccessLevel.PRIVATE)
54 public class ResourceDataOperationRequestUtils {
56 private static final String UNKNOWN_SERVICE_NAME = null;
59 * Create a list of DMI data operation per DMI service (name).
61 * @param topicParamInQuery client given topic
62 * @param requestId unique identifier per request
63 * @param dataOperationRequestIn incoming data operation request details
64 * @param yangModelCmHandles involved cm handles represented as YangModelCmHandle (incl. metadata)
65 * @return {@code Map<String, List<DmiBatchOperation>>} Create a list of DMI batch operation per DMI service (name).
67 public static Map<String, List<DmiDataOperation>> processPerDefinitionInDataOperationsRequest(
68 final String topicParamInQuery,
69 final String requestId,
70 final DataOperationRequest dataOperationRequestIn,
71 final Collection<YangModelCmHandle> yangModelCmHandles) {
73 final Map<String, List<DmiDataOperation>> dmiDataOperationsOutPerDmiServiceName = new HashMap<>();
74 final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus,
75 List<String>>> cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
76 final Set<String> nonReadyCmHandleIdsLookup = filterAndGetNonReadyCmHandleIds(yangModelCmHandles);
78 final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName =
79 DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles);
81 final Map<String, String> dmiServiceNamesPerCmHandleId =
82 getDmiServiceNamesPerCmHandleId(dmiPropertiesPerCmHandleIdPerServiceName);
84 final Map<String, String> moduleSetTagPerCmHandle = getModuleSetTagPerCmHandleId(yangModelCmHandles);
86 for (final DataOperationDefinition dataOperationDefinitionIn :
87 dataOperationRequestIn.getDataOperationDefinitions()) {
88 final List<String> nonExistingCmHandleIds = new ArrayList<>();
89 final List<String> nonReadyCmHandleIds = new ArrayList<>();
90 for (final String cmHandleId : dataOperationDefinitionIn.getCmHandleIds()) {
91 if (nonReadyCmHandleIdsLookup.contains(cmHandleId)) {
92 nonReadyCmHandleIds.add(cmHandleId);
94 final String dmiServiceName = dmiServiceNamesPerCmHandleId.get(cmHandleId);
95 final Map<String, String> cmHandleIdProperties
96 = dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).get(cmHandleId);
97 if (cmHandleIdProperties == null) {
98 nonExistingCmHandleIds.add(cmHandleId);
100 final DmiDataOperation dmiBatchOperationOut = getOrAddDmiBatchOperation(dmiServiceName,
101 dataOperationDefinitionIn, dmiDataOperationsOutPerDmiServiceName);
102 final DmiOperationCmHandle dmiOperationCmHandle = DmiOperationCmHandle
103 .buildDmiOperationCmHandle(cmHandleId, cmHandleIdProperties,
104 moduleSetTagPerCmHandle.get(cmHandleId));
105 dmiBatchOperationOut.getCmHandles().add(dmiOperationCmHandle);
109 populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
110 DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
111 CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds);
112 populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
113 DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
114 CM_HANDLES_NOT_READY, nonReadyCmHandleIds);
116 publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation);
117 return dmiDataOperationsOutPerDmiServiceName;
120 private static Map<String, String> getModuleSetTagPerCmHandleId(
121 final Collection<YangModelCmHandle> yangModelCmHandles) {
122 final Map<String, String> moduleSetTagPerCmHandle = new HashMap<>(yangModelCmHandles.size());
123 yangModelCmHandles.forEach(yangModelCmHandle ->
124 moduleSetTagPerCmHandle.put(yangModelCmHandle.getId(), yangModelCmHandle.getModuleSetTag()));
125 return moduleSetTagPerCmHandle;
129 * Handles the async task completion for an entire data, publishing errors to client topic on task failure.
131 * @param topicParamInQuery client given topic
132 * @param requestId unique identifier per request
133 * @param dataOperationRequest incoming data operation request details
134 * @param throwable error cause, or null if task completed with no exception
136 public static void handleAsyncTaskCompletionForDataOperationsRequest(
137 final String topicParamInQuery,
138 final String requestId,
139 final DataOperationRequest dataOperationRequest,
140 final Throwable throwable) {
141 if (throwable == null) {
142 log.info("Data operations request {} completed.", requestId);
143 } else if (throwable instanceof TimeoutException) {
144 log.error("Data operations request {} timed out.", requestId);
145 ResourceDataOperationRequestUtils.publishErrorMessageToClientTopicForEntireOperation(topicParamInQuery,
146 requestId, dataOperationRequest, NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING);
148 log.error("Data operations request {} failed.", requestId, throwable);
149 ResourceDataOperationRequestUtils.publishErrorMessageToClientTopicForEntireOperation(topicParamInQuery,
150 requestId, dataOperationRequest, NcmpResponseStatus.UNKNOWN_ERROR);
155 * Creates data operation cloud event for when the entire data operation fails and publishes it to client topic.
157 * @param topicParamInQuery client given topic
158 * @param requestId unique identifier per request
159 * @param dataOperationRequestIn incoming data operation request details
160 * @param ncmpResponseStatus response code to be sent for all cm handle ids in all operations
162 private static void publishErrorMessageToClientTopicForEntireOperation(
163 final String topicParamInQuery,
164 final String requestId,
165 final DataOperationRequest dataOperationRequestIn,
166 final NcmpResponseStatus ncmpResponseStatus) {
168 final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>>
169 cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
171 for (final DataOperationDefinition dataOperationDefinitionIn :
172 dataOperationRequestIn.getDataOperationDefinitions()) {
173 cmHandleIdsPerResponseCodesPerOperation.add(
174 DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
175 Map.of(ncmpResponseStatus, dataOperationDefinitionIn.getCmHandleIds()));
177 publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation);
181 * Creates data operation cloud event and publish it to client topic.
183 * @param clientTopic client given topic
184 * @param requestId unique identifier per request
185 * @param cmHandleIdsPerResponseCodesPerOperation list of cm handle ids per operation with response code
187 public static void publishErrorMessageToClientTopic(final String clientTopic,
188 final String requestId,
189 final MultiValueMap<DmiDataOperation,
190 Map<NcmpResponseStatus, List<String>>>
191 cmHandleIdsPerResponseCodesPerOperation) {
192 if (!cmHandleIdsPerResponseCodesPerOperation.isEmpty()) {
193 final CloudEvent dataOperationCloudEvent = DataOperationEventCreator.createDataOperationEvent(clientTopic,
194 requestId, cmHandleIdsPerResponseCodesPerOperation);
195 final EventsPublisher<CloudEvent> eventsPublisher = CpsApplicationContext.getCpsBean(EventsPublisher.class);
196 eventsPublisher.publishCloudEvent(clientTopic, requestId, dataOperationCloudEvent);
200 private static Map<String, String> getDmiServiceNamesPerCmHandleId(
201 final Map<String, Map<String, Map<String, String>>> dmiDmiPropertiesPerCmHandleIdPerServiceName) {
202 final Map<String, String> dmiServiceNamesPerCmHandleId = new HashMap<>();
203 for (final Map.Entry<String, Map<String, Map<String, String>>> dmiDmiPropertiesEntry
204 : dmiDmiPropertiesPerCmHandleIdPerServiceName.entrySet()) {
205 final String dmiServiceName = dmiDmiPropertiesEntry.getKey();
206 final Set<String> cmHandleIds = dmiDmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).keySet();
207 for (final String cmHandleId : cmHandleIds) {
208 dmiServiceNamesPerCmHandleId.put(cmHandleId, dmiServiceName);
211 dmiDmiPropertiesPerCmHandleIdPerServiceName.put(UNKNOWN_SERVICE_NAME, Collections.emptyMap());
212 return dmiServiceNamesPerCmHandleId;
215 private static DmiDataOperation getOrAddDmiBatchOperation(final String dmiServiceName,
216 final DataOperationDefinition
217 dataOperationDefinitionIn,
218 final Map<String, List<DmiDataOperation>>
219 dmiBatchOperationsOutPerDmiServiceName) {
220 dmiBatchOperationsOutPerDmiServiceName
221 .computeIfAbsent(dmiServiceName, dmiServiceNameAsKey -> new ArrayList<>());
222 final List<DmiDataOperation> dmiBatchOperationsOut
223 = dmiBatchOperationsOutPerDmiServiceName.get(dmiServiceName);
224 final boolean isNewOperation = dmiBatchOperationsOut.isEmpty()
225 || !dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1).getOperationId()
226 .equals(dataOperationDefinitionIn.getOperationId());
227 if (isNewOperation) {
228 final DmiDataOperation newDmiBatchOperationOut =
229 DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn);
230 dmiBatchOperationsOut.add(newDmiBatchOperationOut);
231 return newDmiBatchOperationOut;
233 return dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1);
236 private static Set<String> filterAndGetNonReadyCmHandleIds(final Collection<YangModelCmHandle> yangModelCmHandles) {
237 return yangModelCmHandles.stream()
238 .filter(yangModelCmHandle -> yangModelCmHandle.getCompositeState().getCmHandleState()
239 != CmHandleState.READY).map(YangModelCmHandle::getId).collect(Collectors.toSet());
242 private static void populateCmHandleIdsPerOperationIdPerResponseCode(final MultiValueMap<DmiDataOperation,
243 Map<NcmpResponseStatus, List<String>>> cmHandleIdsPerResponseCodesPerOperation,
244 final DmiDataOperation dmiDataOperation,
245 final NcmpResponseStatus
247 final List<String> cmHandleIds) {
248 if (!cmHandleIds.isEmpty()) {
249 cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperation, Map.of(ncmpResponseStatus, cmHandleIds));