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.dmi.rest.stub.controller;
23 import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESS;
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;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
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;
66 @RequestMapping("${rest.api.dmi-stub-base-path}")
67 @RequiredArgsConstructor
69 public class DmiRestStubController {
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;
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
91 * @param requestBody map of cmHandleId and moduleSetTag
92 * @return a ResponseEntity object containing the updated moduleSetTagPerCmHandleId map as the response body
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);
101 * This code defines a GET endpoint of module set tag mapping.
103 * @return The map represents the module set tag mapping.
105 @GetMapping("/v1/tagMapping")
106 public ResponseEntity<Map<String, String>> getTagMapping() {
107 return ResponseEntity.ok(moduleSetTagPerCmHandleId);
111 * This code defines a GET endpoint of module set tag by cm handle ID.
113 * @return The map represents the module set tag mapping filtered by cm handle ID.
115 @GetMapping("/v1/tagMapping/ch/{cmHandleId}")
116 public ResponseEntity<String> getTagMappingByCmHandleId(@PathVariable final String cmHandleId) {
117 return ResponseEntity.ok(moduleSetTagPerCmHandleId.get(cmHandleId));
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
125 * @param requestBody map of cmHandleId and moduleSetTag
126 * @return a ResponseEntity object containing the updated moduleSetTagPerCmHandleId map as the response body
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();
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.
139 * @return a ResponseEntity containing the updated map.
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));
148 * Get all modules for given cm handle.
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.
155 @PostMapping("/v1/ch/{cmHandleId}/modules")
156 public ResponseEntity<String> getModuleReferences(@PathVariable("cmHandleId") final String cmHandleId,
157 @RequestBody final Object moduleReferencesRequest) {
158 delay(moduleReferencesDelayMs);
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");
165 final String moduleResponseContent = getModuleResourceResponse(cmHandleId,
166 "ModuleResponse.json");
167 log.info("cm handle: {} requested for modules", cmHandleId);
168 return ResponseEntity.ok(moduleResponseContent);
172 * Retrieves module resources for a given cmHandleId.
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.
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);
191 * Create resource data from passthrough operational or running for a cm handle.
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
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 @RequestHeader(value = "Authorization", required = false) final String authorization,
209 @RequestBody final String requestBody) {
210 log.info("DMI AUTH HEADER: {}", authorization);
211 delay(dataForCmHandleDelayMs);
212 log.info("Logging request body {}", requestBody);
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);
220 * This method is not implemented for ONAP DMI plugin.
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
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);
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");
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);
248 return new ResponseEntity<>(HttpStatus.ACCEPTED);
251 private CloudEvent buildAndGetCloudEvent(final String topic, final String requestId,
252 final DataOperationEvent dataOperationEvent) {
253 CloudEvent cloudEvent = null;
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)
266 } catch (final JsonProcessingException jsonProcessingException) {
267 log.error("Unable to parse event into bytes. cause : {}", jsonProcessingException.getMessage());
272 private DataOperationEvent getDataOperationEvent(final DataOperationRequest dataOperationRequest) {
273 final Response response = new Response();
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();
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());
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;
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)));
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);
315 private void delay(final long milliseconds) {
317 Thread.sleep(milliseconds);
318 } catch (final InterruptedException e) {
319 log.error("Thread sleep interrupted: {}", e.getMessage());
320 Thread.currentThread().interrupt();