b536c75fb6d55ca66f087a5a791c0ff4a63e9946
[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.List;
32 import java.util.UUID;
33 import lombok.RequiredArgsConstructor;
34 import lombok.extern.slf4j.Slf4j;
35 import org.json.simple.parser.JSONParser;
36 import org.json.simple.parser.ParseException;
37 import org.onap.cps.ncmp.api.impl.utils.EventDateTimeFormatter;
38 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.CmHandle;
39 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest;
40 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiDataOperationRequest;
41 import org.onap.cps.ncmp.dmi.rest.stub.utils.ResourceFileReaderUtil;
42 import org.onap.cps.ncmp.events.async1_0_0.Data;
43 import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent;
44 import org.onap.cps.ncmp.events.async1_0_0.Response;
45 import org.springframework.beans.factory.annotation.Value;
46 import org.springframework.context.ApplicationContext;
47 import org.springframework.core.io.Resource;
48 import org.springframework.core.io.ResourceLoader;
49 import org.springframework.http.HttpStatus;
50 import org.springframework.http.ResponseEntity;
51 import org.springframework.kafka.core.KafkaTemplate;
52 import org.springframework.web.bind.annotation.PathVariable;
53 import org.springframework.web.bind.annotation.PostMapping;
54 import org.springframework.web.bind.annotation.RequestBody;
55 import org.springframework.web.bind.annotation.RequestHeader;
56 import org.springframework.web.bind.annotation.RequestMapping;
57 import org.springframework.web.bind.annotation.RequestParam;
58 import org.springframework.web.bind.annotation.RestController;
59
60 @RestController
61 @RequestMapping("${rest.api.dmi-stub-base-path}")
62 @RequiredArgsConstructor
63 @Slf4j
64 public class DmiRestStubController {
65
66     private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
67     private final ObjectMapper objectMapper;
68     private final ApplicationContext applicationContext;
69
70     @Value("${app.ncmp.async-m2m.topic}")
71     private String ncmpAsyncM2mTopic;
72
73     @Value("${delay.module-references-delay-ms}")
74     private long moduleReferencesDelayMs;
75
76     @Value("${delay.module-resources-delay-ms}")
77     private long moduleResourcesDelayMs;
78
79     @Value("${delay.data-for-cm-handle-delay-ms}")
80     private long dataForCmHandleDelayMs;
81
82     private String dataOperationEventType = "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent";
83
84     /**
85      * Get all modules for given cm handle.
86      *
87      * @param cmHandleId              The identifier for a network function, network element, subnetwork,
88      *                                or any other cm object by managed Network CM Proxy
89      * @param moduleReferencesRequest module references request body
90      * @return ResponseEntity response entity having module response as json string.
91      */
92     @PostMapping("/v1/ch/{cmHandleId}/modules")
93     public ResponseEntity<String> getModuleReferences(@PathVariable final String cmHandleId,
94                                                       @RequestBody final Object moduleReferencesRequest) {
95         delay(moduleReferencesDelayMs);
96         final String moduleResponseContent = getModuleResourceResponse(cmHandleId,
97                 "ModuleResponse.json");
98         log.info("cm handle: {} requested for modules", cmHandleId);
99         return ResponseEntity.ok(moduleResponseContent);
100     }
101
102     /**
103      * Retrieves module resources for a given cmHandleId.
104      *
105      * @param cmHandleId                 The identifier for a network function, network element, subnetwork,
106      *                                   or any other cm object by managed Network CM Proxy
107      * @param moduleResourcesReadRequest module resources read request body
108      * @return ResponseEntity response entity having module resources response as json string.
109      */
110     @PostMapping("/v1/ch/{cmHandleId}/moduleResources")
111     public ResponseEntity<String> retrieveModuleResources(
112             @PathVariable final String cmHandleId,
113             @RequestBody final Object moduleResourcesReadRequest) {
114         delay(moduleResourcesDelayMs);
115         final String moduleResourcesResponseContent = getModuleResourceResponse(cmHandleId,
116                 "ModuleResourcesResponse.json");
117         log.info("cm handle: {} requested for modules resources", cmHandleId);
118         return ResponseEntity.ok(moduleResourcesResponseContent);
119     }
120
121     /**
122      * Create resource data from passthrough operational or running for a cm handle.
123      *
124      * @param cmHandleId              The identifier for a network function, network element, subnetwork,
125      *                                or any other cm object by managed Network CM Proxy
126      * @param datastoreName           datastore name
127      * @param resourceIdentifier      resource identifier
128      * @param options                 options
129      * @param topic                   client given topic name
130      * @return (@ code ResponseEntity) response entity
131      */
132     @PostMapping("/v1/ch/{cmHandleId}/data/ds/{datastoreName}")
133     public ResponseEntity<String> getResourceDataForCmHandle(
134             @PathVariable("cmHandleId") final String cmHandleId,
135             @PathVariable("datastoreName") final String datastoreName,
136             @RequestParam(value = "resourceIdentifier") final String resourceIdentifier,
137             @RequestParam(value = "options", required = false) final String options,
138             @RequestParam(value = "topic", required = false) final String topic,
139             @RequestHeader(value = "Authorization", required = false) final String authorization) {
140         log.info("DMI AUTH HEADER: {}", authorization);
141         delay(dataForCmHandleDelayMs);
142         final String sampleJson = ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
143                 ResourceLoader.CLASSPATH_URL_PREFIX + "data/operational/ietf-network-topology-sample-rfc8345.json"));
144         return ResponseEntity.ok(sampleJson);
145     }
146
147     /**
148      * This method is not implemented for ONAP DMI plugin.
149      *
150      * @param topic                   client given topic name
151      * @param requestId               requestId generated by NCMP as an ack for client
152      * @param dmiDataOperationRequest list of operation details
153      * @return (@ code ResponseEntity) response entity
154      */
155     @PostMapping("/v1/data")
156     public ResponseEntity<Void> getResourceDataForCmHandleDataOperation(
157             @RequestParam(value = "topic") final String topic,
158             @RequestParam(value = "requestId") final String requestId,
159             @RequestBody final DmiDataOperationRequest dmiDataOperationRequest) {
160         delay(dataForCmHandleDelayMs);
161         try {
162             log.info("Request received from the NCMP to DMI Plugin: {}",
163                     objectMapper.writeValueAsString(dmiDataOperationRequest));
164         } catch (final JsonProcessingException jsonProcessingException) {
165             log.info("Unable to process dmi data operation request to json string");
166         }
167         dmiDataOperationRequest.getOperations().forEach(dmiDataOperation -> {
168             final DataOperationEvent dataOperationEvent = getDataOperationEvent(dmiDataOperation);
169             dmiDataOperation.getCmHandles().forEach(cmHandle -> {
170                 dataOperationEvent.getData().getResponses().get(0).setIds(List.of(cmHandle.getId()));
171                 final CloudEvent cloudEvent = buildAndGetCloudEvent(topic, requestId, dataOperationEvent);
172                 cloudEventKafkaTemplate.send(ncmpAsyncM2mTopic, UUID.randomUUID().toString(), cloudEvent);
173             });
174         });
175         return new ResponseEntity<>(HttpStatus.ACCEPTED);
176     }
177
178     private CloudEvent buildAndGetCloudEvent(final String topic, final String requestId,
179                                              final DataOperationEvent dataOperationEvent) {
180         CloudEvent cloudEvent = null;
181         try {
182             cloudEvent = CloudEventBuilder.v1()
183                     .withId(UUID.randomUUID().toString())
184                     .withSource(URI.create("DMI"))
185                     .withType(dataOperationEventType)
186                     .withDataSchema(URI.create("urn:cps:" + dataOperationEventType + ":1.0.0"))
187                     .withTime(EventDateTimeFormatter.toIsoOffsetDateTime(
188                             EventDateTimeFormatter.getCurrentIsoFormattedDateTime()))
189                     .withData(objectMapper.writeValueAsBytes(dataOperationEvent))
190                     .withExtension("destination", topic)
191                     .withExtension("correlationid", requestId)
192                     .build();
193         } catch (final JsonProcessingException jsonProcessingException) {
194             log.error("Unable to parse event into bytes. cause : {}", jsonProcessingException.getMessage());
195         }
196         return cloudEvent;
197     }
198
199     private DataOperationEvent getDataOperationEvent(final DataOperationRequest dataOperationRequest) {
200         final Response response = new Response();
201         response.setOperationId(dataOperationRequest.getOperationId());
202         response.setStatusCode(SUCCESS.getCode());
203         response.setStatusMessage(SUCCESS.getMessage());
204         response.setIds(dataOperationRequest.getCmHandles().stream().map(CmHandle::getId).toList());
205         response.setResourceIdentifier(dataOperationRequest.getResourceIdentifier());
206         response.setOptions(dataOperationRequest.getOptions());
207         final String ietfNetworkTopologySample = ResourceFileReaderUtil
208                 .getResourceFileContent(applicationContext.getResource(
209                         ResourceLoader.CLASSPATH_URL_PREFIX
210                                 + "data/operational/ietf-network-topology-sample-rfc8345.json"));
211         final JSONParser jsonParser = new JSONParser();
212         try {
213             response.setResult(jsonParser.parse(ietfNetworkTopologySample));
214         } catch (final ParseException parseException) {
215             log.error("Unable to parse event result as json object. cause : {}", parseException.getMessage());
216         }
217         final List<Response> responseList = new ArrayList<>(1);
218         responseList.add(response);
219         final Data data = new Data();
220         data.setResponses(responseList);
221         final DataOperationEvent dataOperationEvent = new DataOperationEvent();
222         dataOperationEvent.setData(data);
223         return dataOperationEvent;
224     }
225
226     private String getModuleResourceResponse(final String cmHandleId, final String moduleResponseType) {
227         final String nodeType = cmHandleId.split("-")[0];
228         final String moduleResponseFilePath = String.format("module/%s%s", nodeType, moduleResponseType);
229         final Resource moduleResponseResource = applicationContext.getResource(
230                 ResourceLoader.CLASSPATH_URL_PREFIX + moduleResponseFilePath);
231         if (moduleResponseResource.exists()) {
232             log.info("Using requested node type: {}", nodeType);
233             return ResourceFileReaderUtil.getResourceFileContent(moduleResponseResource);
234         }
235         log.info("Using default node type: ietfYang");
236         return ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
237                 ResourceLoader.CLASSPATH_URL_PREFIX + "module/ietfYang" + moduleResponseType));
238     }
239
240     private void delay(final long milliseconds) {
241         try {
242             Thread.sleep(milliseconds);
243         } catch (final InterruptedException e) {
244             log.error("Thread sleep interrupted: {}", e.getMessage());
245             Thread.currentThread().interrupt();
246         }
247     }
248 }