Async: NCMP Rest impl. including Request ID generation
[cps.git] / cps-ncmp-rest / src / main / java / org / onap / cps / ncmp / rest / controller / NetworkCmProxyController.java
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Pantheon.tech
4  *  Modifications Copyright (C) 2021-2022 Nordix Foundation
5  *  Modification Copyright (C) 2021 highstreet technologies GmbH
6  *  Modifications (C) 2021-2022 Bell Canada
7  *  ================================================================================
8  *  Licensed under the Apache License, Version 2.0 (the "License");
9  *  you may not use this file except in compliance with the License.
10  *  You may obtain a copy of the License at
11  *
12  *        http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *  Unless required by applicable law or agreed to in writing, software
15  *  distributed under the License is distributed on an "AS IS" BASIS,
16  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *  See the License for the specific language governing permissions and
18  *  limitations under the License.
19  *
20  *  SPDX-License-Identifier: Apache-2.0
21  *  ============LICENSE_END=========================================================
22  */
23
24 package org.onap.cps.ncmp.rest.controller;
25
26 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE;
27 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE;
28 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH;
29 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE;
30
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.UUID;
38 import java.util.stream.Collectors;
39 import javax.validation.Valid;
40 import javax.validation.constraints.NotNull;
41 import lombok.RequiredArgsConstructor;
42 import lombok.extern.slf4j.Slf4j;
43 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
44 import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException;
45 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
46 import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
47 import org.onap.cps.ncmp.rest.model.CmHandleProperties;
48 import org.onap.cps.ncmp.rest.model.CmHandleProperty;
49 import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties;
50 import org.onap.cps.ncmp.rest.model.CmHandles;
51 import org.onap.cps.ncmp.rest.model.ConditionProperties;
52 import org.onap.cps.ncmp.rest.model.Conditions;
53 import org.onap.cps.ncmp.rest.model.ModuleNameAsJsonObject;
54 import org.onap.cps.ncmp.rest.model.ModuleNamesAsJsonArray;
55 import org.onap.cps.ncmp.rest.model.RestModuleReference;
56 import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
57 import org.onap.cps.utils.CpsValidator;
58 import org.onap.cps.utils.JsonObjectMapper;
59 import org.springframework.http.HttpStatus;
60 import org.springframework.http.ResponseEntity;
61 import org.springframework.web.bind.annotation.RequestMapping;
62 import org.springframework.web.bind.annotation.RestController;
63
64 @Slf4j
65 @RestController
66 @RequestMapping("${rest.api.ncmp-base-path}")
67 @RequiredArgsConstructor
68 public class NetworkCmProxyController implements NetworkCmProxyApi {
69
70     private static final String NO_BODY = null;
71     private static final String NO_REQUEST_ID = null;
72     private static final String NO_TOPIC = null;
73     public static final String ASYNC_REQUEST_ID = "requestId";
74
75     private final NetworkCmProxyDataService networkCmProxyDataService;
76     private final JsonObjectMapper jsonObjectMapper;
77     private final NcmpRestInputMapper ncmpRestInputMapper;
78
79     /**
80      * Get resource data from operational datastore.
81      *
82      * @param cmHandle cm handle identifier
83      * @param resourceIdentifier resource identifier
84      * @param optionsParamInQuery options query parameter
85      * @param topicParamInQuery topic query parameter
86      * @return {@code ResponseEntity} response from dmi plugin
87      */
88     @Override
89     public ResponseEntity<Object> getResourceDataOperationalForCmHandle(final String cmHandle,
90                                                                         final @NotNull @Valid String resourceIdentifier,
91                                                                         final @Valid String optionsParamInQuery,
92                                                                         final @Valid String topicParamInQuery) {
93         final ResponseEntity<Map<String, Object>> asyncResponse = populateAsyncResponse(topicParamInQuery);
94         final Map<String, Object> asyncResponseData = asyncResponse.getBody();
95
96         final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(cmHandle,
97                 resourceIdentifier,
98                 optionsParamInQuery,
99                 asyncResponseData == null ? NO_TOPIC : topicParamInQuery,
100                 asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString());
101
102         if (asyncResponseData == null) {
103             return ResponseEntity.ok(responseObject);
104         }
105         return ResponseEntity.ok(asyncResponse);
106     }
107
108     /**
109      * Get resource data from pass-through running datastore.
110      *
111      * @param cmHandle cm handle identifier
112      * @param resourceIdentifier resource identifier
113      * @param optionsParamInQuery options query parameter
114      * @param topicParamInQuery topic query parameter
115      * @return {@code ResponseEntity} response from dmi plugin
116      */
117     @Override
118     public ResponseEntity<Object> getResourceDataRunningForCmHandle(final String cmHandle,
119                                                                     final @NotNull @Valid String resourceIdentifier,
120                                                                     final @Valid String optionsParamInQuery,
121                                                                     final @Valid String topicParamInQuery) {
122         final ResponseEntity<Map<String, Object>> asyncResponse = populateAsyncResponse(topicParamInQuery);
123         final Map<String, Object> asyncResponseData = asyncResponse.getBody();
124
125         final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(cmHandle,
126                 resourceIdentifier,
127                 optionsParamInQuery,
128                 asyncResponseData == null ? NO_TOPIC : topicParamInQuery,
129                 asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString());
130
131         if (asyncResponseData == null) {
132             return ResponseEntity.ok(responseObject);
133         }
134         return ResponseEntity.ok(asyncResponse);
135     }
136
137     @Override
138     public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String resourceIdentifier,
139         final String cmHandle,
140         final Object requestBody, final String contentType) {
141         final Object responseObject = networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
142             resourceIdentifier, PATCH, jsonObjectMapper.asJsonString(requestBody), contentType);
143         return ResponseEntity.ok(responseObject);
144     }
145
146     /**
147      * Create resource data in datastore pass-through running for given cm-handle.
148      *
149      * @param resourceIdentifier resource identifier
150      * @param cmHandle cm handle identifier
151      * @param requestBody the request body
152      * @param contentType content type of body
153      * @return {@code ResponseEntity} response from dmi plugin
154      */
155     @Override
156     public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String resourceIdentifier,
157         final String cmHandle, final Object requestBody, final String contentType) {
158         networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
159                 resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType);
160         return new ResponseEntity<>(HttpStatus.CREATED);
161     }
162
163     /**
164      * Update resource data in datastore pass-through running for given cm-handle.
165      *
166      * @param resourceIdentifier resource identifier
167      * @param cmHandle cm handle identifier
168      * @param requestBody the request body
169      * @param contentType content type of the body
170      * @return response entity
171      */
172     @Override
173     public ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String resourceIdentifier,
174                                                                        final String cmHandle,
175                                                                        final Object requestBody,
176                                                                        final String contentType) {
177         networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
178                 resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType);
179         return new ResponseEntity<>(HttpStatus.OK);
180     }
181
182
183     /**
184      *  Delete resource data in datastore pass-through running for a given cm-handle.
185      *
186      * @param resourceIdentifier resource identifier
187      * @param cmHandle cm handle identifier
188      * @param contentType content type of the body
189      * @return response entity no content if request is successful
190      */
191     @Override
192     public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String cmHandle,
193                                                                      final String resourceIdentifier,
194                                                                      final String contentType) {
195         networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
196             resourceIdentifier, DELETE, NO_BODY, contentType);
197         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
198     }
199
200     /**
201      * Execute cm handle search.
202      *
203      * @param conditions the conditions
204      * @return cm handles returned from search.
205      */
206     @Override
207     public ResponseEntity<CmHandles> executeCmHandleSearch(final Conditions conditions) {
208         final List<ConditionProperties> conditionProperties =
209             conditions.getConditions().stream().collect(Collectors.toList());
210         final CmHandles cmHandles = new CmHandles();
211         cmHandles.setCmHandles(toCmHandleProperties(processConditions(conditionProperties)));
212         return ResponseEntity.ok(cmHandles);
213     }
214
215     /**
216      * Search for Cm Handle and Properties by Name.
217      * @param cmHandleId cm-handle identifier
218      * @return cm handle and its properties
219      */
220     @Override
221     public ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleId) {
222         final NcmpServiceCmHandle ncmpServiceCmHandle = networkCmProxyDataService.getNcmpServiceCmHandle(cmHandleId);
223         final RestOutputCmHandle restOutputCmHandle = toRestOutputCmHandle(ncmpServiceCmHandle);
224         return ResponseEntity.ok(restOutputCmHandle);
225     }
226
227     /**
228      * Return module references for a cm handle.
229      *
230      * @param cmHandle the cm handle
231      * @return module references for cm handle. Namespace will be always blank because restConf does not include this.
232      */
233     public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandle) {
234         final List<RestModuleReference> restModuleReferences =
235             networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle).stream()
236             .map(ncmpRestInputMapper::toRestModuleReference)
237                 .collect(Collectors.toList());
238         return new ResponseEntity<>(restModuleReferences, HttpStatus.OK);
239     }
240
241     private Collection<String> processConditions(final List<ConditionProperties> conditionProperties) {
242         for (final ConditionProperties conditionProperty : conditionProperties) {
243             if (conditionProperty.getName().equals("hasAllModules")) {
244                 return executeCmHandleSearchesForModuleNames(conditionProperty);
245             } else {
246                 log.warn("Unrecognized condition name {}.", conditionProperty.getName());
247             }
248         }
249         log.warn("No valid conditions found {}.", conditionProperties);
250         return Collections.emptyList();
251     }
252
253     private Collection<String> executeCmHandleSearchesForModuleNames(final ConditionProperties conditionProperties) {
254         return networkCmProxyDataService
255             .executeCmHandleHasAllModulesSearch(getModuleNames(conditionProperties.getConditionParameters()));
256     }
257
258     private Collection<String> getModuleNames(final ModuleNamesAsJsonArray moduleNamesAsJsonArray) {
259         final Collection<String> moduleNames = new ArrayList<>(moduleNamesAsJsonArray.size());
260         for (final ModuleNameAsJsonObject moduleNameAsJsonObject : moduleNamesAsJsonArray) {
261             moduleNames.add(moduleNameAsJsonObject.getModuleName());
262         }
263         return moduleNames;
264     }
265
266     private CmHandleProperties toCmHandleProperties(final Collection<String> cmHandleIdentifiers) {
267         final CmHandleProperties cmHandleProperties = new CmHandleProperties();
268         for (final String cmHandleIdentifier : cmHandleIdentifiers) {
269             final CmHandleProperty cmHandleProperty = new CmHandleProperty();
270             cmHandleProperty.setCmHandleId(cmHandleIdentifier);
271             cmHandleProperties.add(cmHandleProperty);
272         }
273         return cmHandleProperties;
274     }
275
276     private RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) {
277         final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle();
278         final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
279         restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleID());
280         cmHandlePublicProperties.add(ncmpServiceCmHandle.getPublicProperties());
281         restOutputCmHandle.setPublicCmHandleProperties(cmHandlePublicProperties);
282         return restOutputCmHandle;
283     }
284
285     private ResponseEntity<Map<String, Object>> populateAsyncResponse(final String topicParamInQuery) {
286         final boolean processAsynchronously = hasTopicParameter(topicParamInQuery);
287         final Map<String, Object> responseData;
288         if (processAsynchronously) {
289             responseData = getAsyncResponseData();
290         } else {
291             responseData = null;
292         }
293         return ResponseEntity.ok().body(responseData);
294     }
295
296     private static boolean hasTopicParameter(final String topicName) {
297         if (topicName == null) {
298             return false;
299         }
300         if (CpsValidator.validateTopicName(topicName)) {
301             return true;
302         }
303         throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic");
304     }
305
306     private Map<String, Object> getAsyncResponseData() {
307         final Map<String, Object> asyncResponseData = new HashMap<>(1);
308         final String resourceDataRequestId = UUID.randomUUID().toString();
309         asyncResponseData.put(ASYNC_REQUEST_ID, resourceDataRequestId);
310         return asyncResponseData;
311     }
312
313 }