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.CmHandle;
43 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation;
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 for (final DataOperationDefinition dataOperationDefinitionIn :
85 dataOperationRequestIn.getDataOperationDefinitions()) {
86 final List<String> nonExistingCmHandleIds = new ArrayList<>();
87 final List<String> nonReadyCmHandleIds = new ArrayList<>();
88 for (final String cmHandleId : dataOperationDefinitionIn.getCmHandleIds()) {
89 if (nonReadyCmHandleIdsLookup.contains(cmHandleId)) {
90 nonReadyCmHandleIds.add(cmHandleId);
92 final String dmiServiceName = dmiServiceNamesPerCmHandleId.get(cmHandleId);
93 final Map<String, String> cmHandleIdProperties
94 = dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).get(cmHandleId);
95 if (cmHandleIdProperties == null) {
96 nonExistingCmHandleIds.add(cmHandleId);
98 final DmiDataOperation dmiBatchOperationOut = getOrAddDmiBatchOperation(dmiServiceName,
99 dataOperationDefinitionIn, dmiDataOperationsOutPerDmiServiceName);
100 final CmHandle cmHandle = CmHandle.buildCmHandleWithProperties(cmHandleId,
101 cmHandleIdProperties);
102 dmiBatchOperationOut.getCmHandles().add(cmHandle);
106 populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
107 DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
108 CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds);
109 populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
110 DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
111 CM_HANDLES_NOT_READY, nonReadyCmHandleIds);
113 publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation);
114 return dmiDataOperationsOutPerDmiServiceName;
118 * Handles the async task completion for an entire data, publishing errors to client topic on task failure.
120 * @param topicParamInQuery client given topic
121 * @param requestId unique identifier per request
122 * @param dataOperationRequest incoming data operation request details
123 * @param throwable error cause, or null if task completed with no exception
125 public static void handleAsyncTaskCompletionForDataOperationsRequest(
126 final String topicParamInQuery,
127 final String requestId,
128 final DataOperationRequest dataOperationRequest,
129 final Throwable throwable) {
130 if (throwable == null) {
131 log.info("Data operations request {} completed.", requestId);
132 } else if (throwable instanceof TimeoutException) {
133 log.error("Data operations request {} timed out.", requestId);
134 ResourceDataOperationRequestUtils.publishErrorMessageToClientTopicForEntireOperation(topicParamInQuery,
135 requestId, dataOperationRequest, NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING);
137 log.error("Data operations request {} failed.", requestId, throwable);
138 ResourceDataOperationRequestUtils.publishErrorMessageToClientTopicForEntireOperation(topicParamInQuery,
139 requestId, dataOperationRequest, NcmpResponseStatus.UNKNOWN_ERROR);
144 * Creates data operation cloud event for when the entire data operation fails and publishes it to client topic.
146 * @param topicParamInQuery client given topic
147 * @param requestId unique identifier per request
148 * @param dataOperationRequestIn incoming data operation request details
149 * @param ncmpResponseStatus response code to be sent for all cm handle ids in all operations
151 private static void publishErrorMessageToClientTopicForEntireOperation(
152 final String topicParamInQuery,
153 final String requestId,
154 final DataOperationRequest dataOperationRequestIn,
155 final NcmpResponseStatus ncmpResponseStatus) {
157 final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>>
158 cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
160 for (final DataOperationDefinition dataOperationDefinitionIn :
161 dataOperationRequestIn.getDataOperationDefinitions()) {
162 cmHandleIdsPerResponseCodesPerOperation.add(
163 DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
164 Map.of(ncmpResponseStatus, dataOperationDefinitionIn.getCmHandleIds()));
166 publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation);
170 * Creates data operation cloud event and publish it to client topic.
172 * @param clientTopic client given topic
173 * @param requestId unique identifier per request
174 * @param cmHandleIdsPerResponseCodesPerOperation list of cm handle ids per operation with response code
176 public static void publishErrorMessageToClientTopic(final String clientTopic,
177 final String requestId,
178 final MultiValueMap<DmiDataOperation,
179 Map<NcmpResponseStatus, List<String>>>
180 cmHandleIdsPerResponseCodesPerOperation) {
181 if (!cmHandleIdsPerResponseCodesPerOperation.isEmpty()) {
182 final CloudEvent dataOperationCloudEvent = DataOperationEventCreator.createDataOperationEvent(clientTopic,
183 requestId, cmHandleIdsPerResponseCodesPerOperation);
184 final EventsPublisher<CloudEvent> eventsPublisher = CpsApplicationContext.getCpsBean(EventsPublisher.class);
185 eventsPublisher.publishCloudEvent(clientTopic, requestId, dataOperationCloudEvent);
189 private static Map<String, String> getDmiServiceNamesPerCmHandleId(
190 final Map<String, Map<String, Map<String, String>>> dmiDmiPropertiesPerCmHandleIdPerServiceName) {
191 final Map<String, String> dmiServiceNamesPerCmHandleId = new HashMap<>();
192 for (final Map.Entry<String, Map<String, Map<String, String>>> dmiDmiPropertiesEntry
193 : dmiDmiPropertiesPerCmHandleIdPerServiceName.entrySet()) {
194 final String dmiServiceName = dmiDmiPropertiesEntry.getKey();
195 final Set<String> cmHandleIds = dmiDmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).keySet();
196 for (final String cmHandleId : cmHandleIds) {
197 dmiServiceNamesPerCmHandleId.put(cmHandleId, dmiServiceName);
200 dmiDmiPropertiesPerCmHandleIdPerServiceName.put(UNKNOWN_SERVICE_NAME, Collections.emptyMap());
201 return dmiServiceNamesPerCmHandleId;
204 private static DmiDataOperation getOrAddDmiBatchOperation(final String dmiServiceName,
205 final DataOperationDefinition
206 dataOperationDefinitionIn,
207 final Map<String, List<DmiDataOperation>>
208 dmiBatchOperationsOutPerDmiServiceName) {
209 dmiBatchOperationsOutPerDmiServiceName
210 .computeIfAbsent(dmiServiceName, dmiServiceNameAsKey -> new ArrayList<>());
211 final List<DmiDataOperation> dmiBatchOperationsOut
212 = dmiBatchOperationsOutPerDmiServiceName.get(dmiServiceName);
213 final boolean isNewOperation = dmiBatchOperationsOut.isEmpty()
214 || !dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1).getOperationId()
215 .equals(dataOperationDefinitionIn.getOperationId());
216 if (isNewOperation) {
217 final DmiDataOperation newDmiBatchOperationOut =
218 DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn);
219 dmiBatchOperationsOut.add(newDmiBatchOperationOut);
220 return newDmiBatchOperationOut;
222 return dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1);
225 private static Set<String> filterAndGetNonReadyCmHandleIds(final Collection<YangModelCmHandle> yangModelCmHandles) {
226 return yangModelCmHandles.stream()
227 .filter(yangModelCmHandle -> yangModelCmHandle.getCompositeState().getCmHandleState()
228 != CmHandleState.READY).map(YangModelCmHandle::getId).collect(Collectors.toSet());
231 private static void populateCmHandleIdsPerOperationIdPerResponseCode(final MultiValueMap<DmiDataOperation,
232 Map<NcmpResponseStatus, List<String>>> cmHandleIdsPerResponseCodesPerOperation,
233 final DmiDataOperation dmiDataOperation,
234 final NcmpResponseStatus
236 final List<String> cmHandleIds) {
237 if (!cmHandleIds.isEmpty()) {
238 cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperation, Map.of(ncmpResponseStatus, cmHandleIds));