Update ONAP DMI version in NCMP
[cps.git] / dmi-plugin-demo-and-csit-stub / dmi-plugin-demo-and-csit-stub-service / src / main / java / org / onap / cps / ncmp / dmi / rest / stub / controller / DmiRestStubController.java
1 /*
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
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  *  SPDX-License-Identifier: Apache-2.0
18  *  ============LICENSE_END=========================================================
19  */
20
21 package org.onap.cps.ncmp.dmi.rest.stub.controller;
22
23 import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESS;
24
25 import com.fasterxml.jackson.core.JsonProcessingException;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import io.cloudevents.CloudEvent;
28 import io.cloudevents.core.builder.CloudEventBuilder;
29 import java.net.URI;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.UUID;
35 import lombok.RequiredArgsConstructor;
36 import lombok.extern.slf4j.Slf4j;
37 import org.json.simple.parser.JSONParser;
38 import org.json.simple.parser.ParseException;
39 import org.onap.cps.ncmp.api.impl.utils.EventDateTimeFormatter;
40 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest;
41 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiDataOperationRequest;
42 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiOperationCmHandle;
43 import org.onap.cps.ncmp.dmi.rest.stub.utils.ResourceFileReaderUtil;
44 import org.onap.cps.ncmp.events.async1_0_0.Data;
45 import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent;
46 import org.onap.cps.ncmp.events.async1_0_0.Response;
47 import org.springframework.beans.factory.annotation.Value;
48 import org.springframework.context.ApplicationContext;
49 import org.springframework.core.io.Resource;
50 import org.springframework.core.io.ResourceLoader;
51 import org.springframework.http.HttpStatus;
52 import org.springframework.http.ResponseEntity;
53 import org.springframework.kafka.core.KafkaTemplate;
54 import org.springframework.web.bind.annotation.DeleteMapping;
55 import org.springframework.web.bind.annotation.GetMapping;
56 import org.springframework.web.bind.annotation.PathVariable;
57 import org.springframework.web.bind.annotation.PostMapping;
58 import org.springframework.web.bind.annotation.PutMapping;
59 import org.springframework.web.bind.annotation.RequestBody;
60 import org.springframework.web.bind.annotation.RequestHeader;
61 import org.springframework.web.bind.annotation.RequestMapping;
62 import org.springframework.web.bind.annotation.RequestParam;
63 import org.springframework.web.bind.annotation.RestController;
64
65 @RestController
66 @RequestMapping("${rest.api.dmi-stub-base-path}")
67 @RequiredArgsConstructor
68 @Slf4j
69 public class DmiRestStubController {
70
71     private static final String DEFAULT_TAG = "tagD";
72     private static final String dataOperationEventType = "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent";
73     private static final Map<String, String> moduleSetTagPerCmHandleId = new HashMap<>();
74     private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
75     private final ObjectMapper objectMapper;
76     private final ApplicationContext applicationContext;
77     @Value("${app.ncmp.async-m2m.topic}")
78     private String ncmpAsyncM2mTopic;
79     @Value("${delay.module-references-delay-ms}")
80     private long moduleReferencesDelayMs;
81     @Value("${delay.module-resources-delay-ms}")
82     private long moduleResourcesDelayMs;
83     @Value("${delay.data-for-cm-handle-delay-ms}")
84     private long dataForCmHandleDelayMs;
85
86     /**
87      * This code defines a REST API endpoint for adding new the module set tag mapping. The endpoint receives the
88      * cmHandleId and moduleSetTag as request body and add into moduleSetTagPerCmHandleId map with the provided
89      * values.
90      *
91      * @param requestBody map of cmHandleId and moduleSetTag
92      * @return a ResponseEntity object containing the updated moduleSetTagPerCmHandleId map as the response body
93      */
94     @PostMapping("/v1/tagMapping")
95     public ResponseEntity<Map<String, String>> addTagForMapping(@RequestBody final Map<String, String> requestBody) {
96         moduleSetTagPerCmHandleId.putAll(requestBody);
97         return new ResponseEntity<>(requestBody, HttpStatus.CREATED);
98     }
99
100     /**
101      * This code defines a GET endpoint of  module set tag mapping.
102      *
103      * @return The map represents the module set tag mapping.
104      */
105     @GetMapping("/v1/tagMapping")
106     public ResponseEntity<Map<String, String>> getTagMapping() {
107         return ResponseEntity.ok(moduleSetTagPerCmHandleId);
108     }
109
110     /**
111      * This code defines a GET endpoint of  module set tag by cm handle ID.
112      *
113      * @return The map represents the module set tag mapping filtered by cm handle ID.
114      */
115     @GetMapping("/v1/tagMapping/ch/{cmHandleId}")
116     public ResponseEntity<String> getTagMappingByCmHandleId(@PathVariable final String cmHandleId) {
117         return ResponseEntity.ok(moduleSetTagPerCmHandleId.get(cmHandleId));
118     }
119
120     /**
121      * This code defines a REST API endpoint for updating the module set tag mapping. The endpoint receives the
122      * cmHandleId and moduleSetTag as request body and updates the moduleSetTagPerCmHandleId map with the provided
123      * values.
124      *
125      * @param requestBody map of cmHandleId and moduleSetTag
126      * @return a ResponseEntity object containing the updated moduleSetTagPerCmHandleId map as the response body
127      */
128
129     @PutMapping("/v1/tagMapping")
130     public ResponseEntity<Map<String, String>> updateTagMapping(@RequestBody final Map<String, String> requestBody) {
131         moduleSetTagPerCmHandleId.putAll(requestBody);
132         return ResponseEntity.noContent().build();
133     }
134
135     /**
136      * It contains a method to delete an entry from the moduleSetTagPerCmHandleId map.
137      * The method takes a cmHandleId as a parameter and removes the corresponding entry from the map.
138      *
139      * @return a ResponseEntity containing the updated map.
140      */
141     @DeleteMapping("/v1/tagMapping/ch/{cmHandleId}")
142     public ResponseEntity<String> deleteTagMappingByCmHandleId(@PathVariable final String cmHandleId) {
143         moduleSetTagPerCmHandleId.remove(cmHandleId);
144         return ResponseEntity.ok(String.format("Mapping of %s is deleted successfully", cmHandleId));
145     }
146
147     /**
148      * Get all modules for given cm handle.
149      *
150      * @param cmHandleId              The identifier for a network function, network element, subnetwork,
151      *                                or any other cm object by managed Network CM Proxy
152      * @param moduleReferencesRequest module references request body
153      * @return ResponseEntity response entity having module response as json string.
154      */
155     @PostMapping("/v1/ch/{cmHandleId}/modules")
156     public ResponseEntity<String> getModuleReferences(@PathVariable("cmHandleId") final String cmHandleId,
157                                                       @RequestBody final Object moduleReferencesRequest) {
158         delay(moduleReferencesDelayMs);
159         try {
160             log.info("Incoming DMI request body: {}",
161                     objectMapper.writeValueAsString(moduleReferencesRequest));
162         } catch (final JsonProcessingException jsonProcessingException) {
163             log.info("Unable to parse dmi data operation request to json string");
164         }
165         final String moduleResponseContent = getModuleResourceResponse(cmHandleId,
166                 "ModuleResponse.json");
167         log.info("cm handle: {} requested for modules", cmHandleId);
168         return ResponseEntity.ok(moduleResponseContent);
169     }
170
171     /**
172      * Retrieves module resources for a given cmHandleId.
173      *
174      * @param cmHandleId                 The identifier for a network function, network element, subnetwork,
175      *                                   or any other cm object by managed Network CM Proxy
176      * @param moduleResourcesReadRequest module resources read request body
177      * @return ResponseEntity response entity having module resources response as json string.
178      */
179     @PostMapping("/v1/ch/{cmHandleId}/moduleResources")
180     public ResponseEntity<String> retrieveModuleResources(
181             @PathVariable("cmHandleId") final String cmHandleId,
182             @RequestBody final Object moduleResourcesReadRequest) {
183         delay(moduleResourcesDelayMs);
184         final String moduleResourcesResponseContent = getModuleResourceResponse(cmHandleId,
185                 "ModuleResourcesResponse.json");
186         log.info("cm handle: {} requested for modules resources", cmHandleId);
187         return ResponseEntity.ok(moduleResourcesResponseContent);
188     }
189
190     /**
191      * Create resource data from passthrough operational or running for a cm handle.
192      *
193      * @param cmHandleId              The identifier for a network function, network element, subnetwork,
194      *                                or any other cm object by managed Network CM Proxy
195      * @param datastoreName           datastore name
196      * @param resourceIdentifier      resource identifier
197      * @param options                 options
198      * @param topic                   client given topic name
199      * @return (@ code ResponseEntity) response entity
200      */
201     @PostMapping("/v1/ch/{cmHandleId}/data/ds/{datastoreName}")
202     public ResponseEntity<String> getResourceDataForCmHandle(
203             @PathVariable("cmHandleId") final String cmHandleId,
204             @PathVariable("datastoreName") final String datastoreName,
205             @RequestParam(value = "resourceIdentifier") final String resourceIdentifier,
206             @RequestParam(value = "options", required = false) final String options,
207             @RequestParam(value = "topic", required = false) final String topic,
208             @RequestParam(value = "moduleSetTag", required = false) final String moduleSetTag,
209             @RequestHeader(value = "Authorization", required = false) final String authorization) {
210         log.info("DMI AUTH HEADER: {}", authorization);
211         delay(dataForCmHandleDelayMs);
212         log.info("Module set tag received: {}", moduleSetTag);
213
214         final String sampleJson = ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
215                 ResourceLoader.CLASSPATH_URL_PREFIX + "data/operational/ietf-network-topology-sample-rfc8345.json"));
216         return ResponseEntity.ok(sampleJson);
217     }
218
219     /**
220      * This method is not implemented for ONAP DMI plugin.
221      *
222      * @param topic                   client given topic name
223      * @param requestId               requestId generated by NCMP as an ack for client
224      * @param dmiDataOperationRequest list of operation details
225      * @return (@ code ResponseEntity) response entity
226      */
227     @PostMapping("/v1/data")
228     public ResponseEntity<Void> getResourceDataForCmHandleDataOperation(
229             @RequestParam(value = "topic") final String topic,
230             @RequestParam(value = "requestId") final String requestId,
231             @RequestBody final DmiDataOperationRequest dmiDataOperationRequest) {
232         delay(dataForCmHandleDelayMs);
233         try {
234             log.info("Request received from the NCMP to DMI Plugin: {}",
235                     objectMapper.writeValueAsString(dmiDataOperationRequest));
236         } catch (final JsonProcessingException jsonProcessingException) {
237             log.info("Unable to process dmi data operation request to json string");
238         }
239         dmiDataOperationRequest.getOperations().forEach(dmiDataOperation -> {
240             final DataOperationEvent dataOperationEvent = getDataOperationEvent(dmiDataOperation);
241             dmiDataOperation.getCmHandles().forEach(dmiOperationCmHandle -> {
242                 log.info("Module Set Tag received: {}", dmiOperationCmHandle.getModuleSetTag());
243                 dataOperationEvent.getData().getResponses().get(0).setIds(List.of(dmiOperationCmHandle.getId()));
244                 final CloudEvent cloudEvent = buildAndGetCloudEvent(topic, requestId, dataOperationEvent);
245                 cloudEventKafkaTemplate.send(ncmpAsyncM2mTopic, UUID.randomUUID().toString(), cloudEvent);
246             });
247         });
248         return new ResponseEntity<>(HttpStatus.ACCEPTED);
249     }
250
251     private CloudEvent buildAndGetCloudEvent(final String topic, final String requestId,
252                                              final DataOperationEvent dataOperationEvent) {
253         CloudEvent cloudEvent = null;
254         try {
255             cloudEvent = CloudEventBuilder.v1()
256                     .withId(UUID.randomUUID().toString())
257                     .withSource(URI.create("DMI"))
258                     .withType(dataOperationEventType)
259                     .withDataSchema(URI.create("urn:cps:" + dataOperationEventType + ":1.0.0"))
260                     .withTime(EventDateTimeFormatter.toIsoOffsetDateTime(
261                             EventDateTimeFormatter.getCurrentIsoFormattedDateTime()))
262                     .withData(objectMapper.writeValueAsBytes(dataOperationEvent))
263                     .withExtension("destination", topic)
264                     .withExtension("correlationid", requestId)
265                     .build();
266         } catch (final JsonProcessingException jsonProcessingException) {
267             log.error("Unable to parse event into bytes. cause : {}", jsonProcessingException.getMessage());
268         }
269         return cloudEvent;
270     }
271
272     private DataOperationEvent getDataOperationEvent(final DataOperationRequest dataOperationRequest) {
273         final Response response = new Response();
274
275         response.setOperationId(dataOperationRequest.getOperationId());
276         response.setStatusCode(SUCCESS.getCode());
277         response.setStatusMessage(SUCCESS.getMessage());
278         response.setIds(dataOperationRequest.getCmHandles().stream().map(DmiOperationCmHandle::getId).toList());
279         response.setResourceIdentifier(dataOperationRequest.getResourceIdentifier());
280         response.setOptions(dataOperationRequest.getOptions());
281         final String ietfNetworkTopologySample = ResourceFileReaderUtil
282                 .getResourceFileContent(applicationContext.getResource(
283                         ResourceLoader.CLASSPATH_URL_PREFIX
284                                 + "data/operational/ietf-network-topology-sample-rfc8345.json"));
285         final JSONParser jsonParser = new JSONParser();
286         try {
287             response.setResult(jsonParser.parse(ietfNetworkTopologySample));
288         } catch (final ParseException parseException) {
289             log.error("Unable to parse event result as json object. cause : {}", parseException.getMessage());
290         }
291         final List<Response> responseList = new ArrayList<>(1);
292         responseList.add(response);
293         final Data data = new Data();
294         data.setResponses(responseList);
295         final DataOperationEvent dataOperationEvent = new DataOperationEvent();
296         dataOperationEvent.setData(data);
297         return dataOperationEvent;
298     }
299
300     private String getModuleResourceResponse(final String cmHandleId, final String moduleResponseType) {
301         if (moduleSetTagPerCmHandleId.isEmpty()) {
302             log.info("Using default module responses of type ietfYang");
303             return ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
304                     ResourceLoader.CLASSPATH_URL_PREFIX
305                             + String.format("module/ietfYang-%s", moduleResponseType)));
306         }
307         final String moduleSetTag = moduleSetTagPerCmHandleId.getOrDefault(cmHandleId, DEFAULT_TAG);
308         final String moduleResponseFilePath = String.format("module/%s-%s", moduleSetTag, moduleResponseType);
309         final Resource moduleResponseResource = applicationContext.getResource(
310                 ResourceLoader.CLASSPATH_URL_PREFIX + moduleResponseFilePath);
311         log.info("Using module responses from : {}", moduleResponseFilePath);
312         return ResourceFileReaderUtil.getResourceFileContent(moduleResponseResource);
313     }
314
315     private void delay(final long milliseconds) {
316         try {
317             Thread.sleep(milliseconds);
318         } catch (final InterruptedException e) {
319             log.error("Thread sleep interrupted: {}", e.getMessage());
320             Thread.currentThread().interrupt();
321         }
322     }
323 }