summaryrefslogtreecommitdiffstats
path: root/dmi-stub/dmi-stub-service/src/main/java
diff options
context:
space:
mode:
authorsourabh_sourabh <sourabh.sourabh@est.tech>2024-11-20 14:43:25 +0000
committersourabh_sourabh <sourabh.sourabh@est.tech>2024-11-20 15:43:50 +0000
commit9ad11f2914b10b3d5049aa8dde050c998b1f9f82 (patch)
tree4520cf3d4b3d5e6bb1ef5e8b9cd87136011aa9dd /dmi-stub/dmi-stub-service/src/main/java
parent38becbcbbc5552150133ee1b4b477903f0f1e6c6 (diff)
[DMI] Fix for DMI merge master Job
- module names of onap dmi and stub is changed - reverted to 1.6.0-SNAPSHOT Issue-ID: CPS-2499 Change-Id: I2ca6f98ceb38bfc366997358ab9ecb0624098ac9 Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
Diffstat (limited to 'dmi-stub/dmi-stub-service/src/main/java')
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java417
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcess.java34
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcessAspect.java107
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DataOperationRequest.java39
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiDataOperationRequest.java33
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiOperationCmHandle.java34
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReference.java26
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReferences.java27
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleResource.java27
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/service/YangModuleFactory.java161
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/EventDateTimeFormatter.java47
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ModuleResponseType.java27
-rw-r--r--dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ResourceFileReaderUtil.java51
13 files changed, 1030 insertions, 0 deletions
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
new file mode 100644
index 00000000..cf18f239
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
@@ -0,0 +1,417 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023-2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.controller;
+
+import static org.onap.cps.ncmp.dmi.rest.stub.utils.ModuleResponseType.MODULE_REFERENCE_RESPONSE;
+import static org.onap.cps.ncmp.dmi.rest.stub.utils.ModuleResponseType.MODULE_RESOURCE_RESPONSE;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.cloudevents.CloudEvent;
+import io.cloudevents.core.builder.CloudEventBuilder;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.onap.cps.ncmp.dmi.datajobs.model.SubjobWriteRequest;
+import org.onap.cps.ncmp.dmi.datajobs.model.SubjobWriteResponse;
+import org.onap.cps.ncmp.dmi.rest.stub.controller.aop.ModuleInitialProcess;
+import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest;
+import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiDataOperationRequest;
+import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiOperationCmHandle;
+import org.onap.cps.ncmp.dmi.rest.stub.service.YangModuleFactory;
+import org.onap.cps.ncmp.dmi.rest.stub.utils.EventDateTimeFormatter;
+import org.onap.cps.ncmp.dmi.rest.stub.utils.ModuleResponseType;
+import org.onap.cps.ncmp.dmi.rest.stub.utils.ResourceFileReaderUtil;
+import org.onap.cps.ncmp.events.async1_0_0.Data;
+import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent;
+import org.onap.cps.ncmp.events.async1_0_0.Response;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("${rest.api.dmi-stub-base-path}")
+@Slf4j
+@RequiredArgsConstructor
+public class DmiRestStubController {
+
+ private static final String DEFAULT_PASSTHROUGH_OPERATION = "read";
+ private static final String DATA_OPERATION_EVENT_TYPE = "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent";
+ private static final Map<String, String> moduleSetTagPerCmHandleId = new HashMap<>();
+ private static final List<String> MODULE_SET_TAGS = YangModuleFactory.generateTags();
+ private static final String DEFAULT_TAG = "tagDefault";
+
+ private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
+ private final ObjectMapper objectMapper;
+ private final ApplicationContext applicationContext;
+ private final AtomicInteger subJobWriteRequestCounter = new AtomicInteger();
+ private final YangModuleFactory yangModuleFactory;
+
+ @Value("${app.ncmp.async-m2m.topic}")
+ private String ncmpAsyncM2mTopic;
+ @Value("${delay.module-references-delay-ms}")
+ private long moduleReferencesDelayMs;
+ @Value("${delay.module-resources-delay-ms}")
+ private long moduleResourcesDelayMs;
+ @Value("${delay.read-data-for-cm-handle-delay-ms}")
+ private long readDataForCmHandleDelayMs;
+ @Value("${delay.write-data-for-cm-handle-delay-ms}")
+ private long writeDataForCmHandleDelayMs;
+
+ /**
+ * This code defines a REST API endpoint for adding new the module set tag mapping. The endpoint receives the
+ * cmHandleId and moduleSetTag as request body and add into moduleSetTagPerCmHandleId map with the provided
+ * values.
+ *
+ * @param requestBody map of cmHandleId and moduleSetTag
+ * @return a ResponseEntity object containing the updated moduleSetTagPerCmHandleId map as the response body
+ */
+ @PostMapping("/v1/tagMapping")
+ public ResponseEntity<Map<String, String>> addTagForMapping(@RequestBody final Map<String, String> requestBody) {
+ moduleSetTagPerCmHandleId.putAll(requestBody);
+ return new ResponseEntity<>(requestBody, HttpStatus.CREATED);
+ }
+
+ /**
+ * This code defines a GET endpoint of module set tag mapping.
+ *
+ * @return The map represents the module set tag mapping.
+ */
+ @GetMapping("/v1/tagMapping")
+ public ResponseEntity<Map<String, String>> getTagMapping() {
+ return ResponseEntity.ok(moduleSetTagPerCmHandleId);
+ }
+
+ /**
+ * This code defines a GET endpoint of module set tag by cm handle ID.
+ *
+ * @return The map represents the module set tag mapping filtered by cm handle ID.
+ */
+ @GetMapping("/v1/tagMapping/ch/{cmHandleId}")
+ public ResponseEntity<String> getTagMappingByCmHandleId(@PathVariable final String cmHandleId) {
+ return ResponseEntity.ok(moduleSetTagPerCmHandleId.get(cmHandleId));
+ }
+
+ /**
+ * This code defines a REST API endpoint for updating the module set tag mapping. The endpoint receives the
+ * cmHandleId and moduleSetTag as request body and updates the moduleSetTagPerCmHandleId map with the provided
+ * values.
+ *
+ * @param requestBody map of cmHandleId and moduleSetTag
+ * @return a ResponseEntity object containing the updated moduleSetTagPerCmHandleId map as the response body
+ */
+
+ @PutMapping("/v1/tagMapping")
+ public ResponseEntity<Map<String, String>> updateTagMapping(@RequestBody final Map<String, String> requestBody) {
+ moduleSetTagPerCmHandleId.putAll(requestBody);
+ return ResponseEntity.noContent().build();
+ }
+
+ /**
+ * It contains a method to delete an entry from the moduleSetTagPerCmHandleId map.
+ * The method takes a cmHandleId as a parameter and removes the corresponding entry from the map.
+ *
+ * @return a ResponseEntity containing the updated map.
+ */
+ @DeleteMapping("/v1/tagMapping/ch/{cmHandleId}")
+ public ResponseEntity<String> deleteTagMappingByCmHandleId(@PathVariable final String cmHandleId) {
+ moduleSetTagPerCmHandleId.remove(cmHandleId);
+ return ResponseEntity.ok(String.format("Mapping of %s is deleted successfully", cmHandleId));
+ }
+
+ /**
+ * Get all modules for given cm handle.
+ *
+ * @param cmHandleId The identifier for a network function, network element, subnetwork,
+ * or any other cm object by managed Network CM Proxy
+ * @param moduleReferencesRequest module references request body
+ * @return ResponseEntity response entity having module response as json string.
+ */
+ @PostMapping("/v1/ch/{cmHandleId}/modules")
+ @ModuleInitialProcess
+ public ResponseEntity<String> getModuleReferences(@PathVariable("cmHandleId") final String cmHandleId,
+ @RequestBody final Object moduleReferencesRequest) {
+ return processModuleRequest(moduleReferencesRequest, MODULE_REFERENCE_RESPONSE, moduleReferencesDelayMs);
+ }
+
+ /**
+ * Get module resources for a given cmHandleId.
+ *
+ * @param cmHandleId The identifier for a network function, network element, subnetwork,
+ * or any other cm object by managed Network CM Proxy
+ * @param moduleResourcesReadRequest module resources read request body
+ * @return ResponseEntity response entity having module resources response as json string.
+ */
+ @PostMapping("/v1/ch/{cmHandleId}/moduleResources")
+ @ModuleInitialProcess
+ public ResponseEntity<String> getModuleResources(
+ @PathVariable("cmHandleId") final String cmHandleId,
+ @RequestBody final Object moduleResourcesReadRequest) {
+ return processModuleRequest(moduleResourcesReadRequest, MODULE_RESOURCE_RESPONSE, moduleResourcesDelayMs);
+ }
+
+ /**
+ * Create resource data from passthrough operational or running for a cm handle.
+ *
+ * @param cmHandleId The identifier for a network function, network element, subnetwork,
+ * or any other cm object by managed Network CM Proxy
+ * @param datastoreName datastore name
+ * @param resourceIdentifier resource identifier
+ * @param options options
+ * @param topic client given topic name
+ * @return (@ code ResponseEntity) response entity
+ */
+ @PostMapping("/v1/ch/{cmHandleId}/data/ds/{datastoreName}")
+ public ResponseEntity<String> getResourceDataForCmHandle(
+ @PathVariable("cmHandleId") final String cmHandleId,
+ @PathVariable("datastoreName") final String datastoreName,
+ @RequestParam(value = "resourceIdentifier") final String resourceIdentifier,
+ @RequestParam(value = "options", required = false) final String options,
+ @RequestParam(value = "topic", required = false) final String topic,
+ @RequestHeader(value = "Authorization", required = false) final String authorization,
+ @RequestBody final String requestBody) {
+ log.info("DMI AUTH HEADER: {}", authorization);
+ final String passthroughOperationType = getPassthroughOperationType(requestBody);
+ if (passthroughOperationType.equals("read")) {
+ delay(readDataForCmHandleDelayMs);
+ } else {
+ delay(writeDataForCmHandleDelayMs);
+ }
+ log.info("Logging request body {}", requestBody);
+
+ final String sampleJson = ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
+ ResourceLoader.CLASSPATH_URL_PREFIX + "data/ietf-network-topology-sample-rfc8345.json"));
+ return ResponseEntity.ok(sampleJson);
+ }
+
+ /**
+ * This method is not implemented for ONAP DMI plugin.
+ *
+ * @param topic client given topic name
+ * @param requestId requestId generated by NCMP as an ack for client
+ * @param dmiDataOperationRequest list of operation details
+ * @return (@ code ResponseEntity) response entity
+ */
+ @PostMapping("/v1/data")
+ public ResponseEntity<Void> getResourceDataForCmHandleDataOperation(
+ @RequestParam(value = "topic") final String topic,
+ @RequestParam(value = "requestId") final String requestId,
+ @RequestBody final DmiDataOperationRequest dmiDataOperationRequest) {
+ delay(writeDataForCmHandleDelayMs);
+ try {
+ log.info("Request received from the NCMP to DMI Plugin: {}",
+ objectMapper.writeValueAsString(dmiDataOperationRequest));
+ } catch (final JsonProcessingException jsonProcessingException) {
+ log.info("Unable to process dmi data operation request to json string");
+ }
+ dmiDataOperationRequest.getOperations().forEach(dmiDataOperation -> {
+ final DataOperationEvent dataOperationEvent = getDataOperationEvent(dmiDataOperation);
+ dmiDataOperation.getCmHandles().forEach(dmiOperationCmHandle -> {
+ log.info("Module Set Tag received: {}", dmiOperationCmHandle.getModuleSetTag());
+ dataOperationEvent.getData().getResponses().get(0).setIds(List.of(dmiOperationCmHandle.getId()));
+ final CloudEvent cloudEvent = buildAndGetCloudEvent(topic, requestId, dataOperationEvent);
+ cloudEventKafkaTemplate.send(ncmpAsyncM2mTopic, UUID.randomUUID().toString(), cloudEvent);
+ });
+ });
+ return new ResponseEntity<>(HttpStatus.ACCEPTED);
+ }
+
+ /**
+ * Consume sub-job write requests from NCMP.
+ *
+ * @param subJobWriteRequest contains a collection of write requests and metadata.
+ * @param destination the destination of the results. ( e.g. S3 Bucket).
+ * @return (@ code ResponseEntity) response for the write request.
+ */
+ @PostMapping("/v1/cmwriteJob")
+ public ResponseEntity<SubjobWriteResponse> consumeWriteSubJobs(
+ @RequestBody final SubjobWriteRequest subJobWriteRequest,
+ @RequestParam("destination") final String destination) {
+ log.debug("Destination: {}", destination);
+ log.debug("Request body: {}", subJobWriteRequest);
+ return ResponseEntity.ok(new SubjobWriteResponse(String.valueOf(subJobWriteRequestCounter.incrementAndGet()),
+ "some-dmi-service-name", "my-data-producer-id"));
+ }
+
+ /**
+ * Retrieves the status of a given data job identified by {@code requestId} and {@code dataProducerJobId}.
+ *
+ * @param dataProducerId ID of the producer registered by DMI for the alternateIDs
+ * in the operations in this request.
+ * @param dataProducerJobId Identifier of the data producer job.
+ * @return A ResponseEntity with HTTP status 200 (OK) and the data job's status as a string.
+ */
+ @GetMapping("/v1/cmwriteJob/dataProducer/{dataProducerId}/dataProducerJob/{dataProducerJobId}/status")
+ public ResponseEntity<Map<String, String>> retrieveDataJobStatus(
+ @PathVariable("dataProducerId") final String dataProducerId,
+ @PathVariable("dataProducerJobId") final String dataProducerJobId) {
+ log.info("Received request to retrieve data job status. Request ID: {}, Data Producer Job ID: {}",
+ dataProducerId, dataProducerJobId);
+ return ResponseEntity.ok(Map.of("status", "FINISHED"));
+ }
+
+ /**
+ * Retrieves the result of a given data job identified by {@code requestId} and {@code dataProducerJobId}.
+ *
+ * @param dataProducerId Identifier for the data producer as a query parameter (required)
+ * @param dataProducerJobId Identifier for the data producer job (required)
+ * @param destination The destination of the results, Kafka topic name or s3 bucket name (required)
+ * @return A ResponseEntity with HTTP status 200 (OK) and the data job's result as an Object.
+ */
+ @GetMapping("/v1/cmwriteJob/dataProducer/{dataProducerId}/dataProducerJob/{dataProducerJobId}/result")
+ public ResponseEntity<Object> retrieveDataJobResult(
+ @PathVariable("dataProducerId") final String dataProducerId,
+ @PathVariable("dataProducerJobId") final String dataProducerJobId,
+ @RequestParam(name = "destination") final String destination) {
+ log.debug("Received request to retrieve data job result. Data Producer ID: {}, "
+ + "Data Producer Job ID: {}, Destination: {}",
+ dataProducerId, dataProducerJobId, destination);
+ return ResponseEntity.ok(Map.of("result", "some status"));
+ }
+
+ private CloudEvent buildAndGetCloudEvent(final String topic, final String requestId,
+ final DataOperationEvent dataOperationEvent) {
+ CloudEvent cloudEvent = null;
+ try {
+ cloudEvent = CloudEventBuilder.v1()
+ .withId(UUID.randomUUID().toString())
+ .withSource(URI.create("DMI"))
+ .withType(DATA_OPERATION_EVENT_TYPE)
+ .withDataSchema(URI.create("urn:cps:" + DATA_OPERATION_EVENT_TYPE + ":1.0.0"))
+ .withTime(EventDateTimeFormatter.toIsoOffsetDateTime(
+ EventDateTimeFormatter.getCurrentIsoFormattedDateTime()))
+ .withData(objectMapper.writeValueAsBytes(dataOperationEvent))
+ .withExtension("destination", topic)
+ .withExtension("correlationid", requestId)
+ .build();
+ } catch (final JsonProcessingException jsonProcessingException) {
+ log.error("Unable to parse event into bytes. cause : {}", jsonProcessingException.getMessage());
+ }
+ return cloudEvent;
+ }
+
+ private DataOperationEvent getDataOperationEvent(final DataOperationRequest dataOperationRequest) {
+ final Response response = new Response();
+
+ response.setOperationId(dataOperationRequest.getOperationId());
+ response.setStatusCode("0");
+ response.setStatusMessage("Successfully applied changes");
+ response.setIds(dataOperationRequest.getCmHandles().stream().map(DmiOperationCmHandle::getId)
+ .collect(Collectors.toList()));
+ response.setResourceIdentifier(dataOperationRequest.getResourceIdentifier());
+ response.setOptions(dataOperationRequest.getOptions());
+ final String ietfNetworkTopologySample = ResourceFileReaderUtil.getResourceFileContent(
+ applicationContext.getResource(ResourceLoader.CLASSPATH_URL_PREFIX
+ + "data/ietf-network-topology-sample-rfc8345.json"));
+ final JSONParser jsonParser = new JSONParser();
+ try {
+ response.setResult(jsonParser.parse(ietfNetworkTopologySample));
+ } catch (final ParseException parseException) {
+ log.error("Unable to parse event result as json object. cause : {}", parseException.getMessage());
+ }
+ final List<Response> responseList = new ArrayList<>(1);
+ responseList.add(response);
+ final Data data = new Data();
+ data.setResponses(responseList);
+ final DataOperationEvent dataOperationEvent = new DataOperationEvent();
+ dataOperationEvent.setData(data);
+ return dataOperationEvent;
+ }
+
+ private ResponseEntity<String> processModuleRequest(final Object moduleRequest,
+ final ModuleResponseType moduleResponseType,
+ final long simulatedResponseDelay) {
+ logRequestBody(moduleRequest);
+ String moduleResponseContent = "";
+ String moduleSetTag = extractModuleSetTagFromRequest(moduleRequest);
+
+ moduleSetTag = (!isModuleSetTagNullOrEmpty(moduleSetTag)
+ && MODULE_SET_TAGS.contains(moduleSetTag)) ? moduleSetTag : DEFAULT_TAG;
+
+ if (MODULE_RESOURCE_RESPONSE == moduleResponseType) {
+ moduleResponseContent = yangModuleFactory.getModuleResourcesJson(moduleSetTag);
+ } else {
+ moduleResponseContent = yangModuleFactory.getModuleReferencesJson(moduleSetTag);
+ }
+
+ delay(simulatedResponseDelay);
+ return ResponseEntity.ok(moduleResponseContent);
+ }
+
+ private String extractModuleSetTagFromRequest(final Object moduleReferencesRequest) {
+ final JsonNode rootNode = objectMapper.valueToTree(moduleReferencesRequest);
+ return rootNode.path("moduleSetTag").asText(null);
+ }
+
+ private boolean isModuleSetTagNullOrEmpty(final String moduleSetTag) {
+ return moduleSetTag == null || moduleSetTag.trim().isEmpty();
+ }
+
+ private void logRequestBody(final Object request) {
+ try {
+ log.info("Incoming DMI request body: {}", objectMapper.writeValueAsString(request));
+ } catch (final JsonProcessingException jsonProcessingException) {
+ log.info("Unable to parse DMI request to json string");
+ }
+ }
+
+ private String getPassthroughOperationType(final String requestBody) {
+ try {
+ final JsonNode rootNode = objectMapper.readTree(requestBody);
+ return rootNode.path("operation").asText();
+ } catch (final JsonProcessingException jsonProcessingException) {
+ log.error("Invalid JSON format. cause : {}", jsonProcessingException.getMessage());
+ }
+ return DEFAULT_PASSTHROUGH_OPERATION;
+ }
+
+ private void delay(final long milliseconds) {
+ try {
+ Thread.sleep(milliseconds);
+ } catch (final InterruptedException e) {
+ log.error("Thread sleep interrupted: {}", e.getMessage());
+ Thread.currentThread().interrupt();
+ }
+ }
+} \ No newline at end of file
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcess.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcess.java
new file mode 100644
index 00000000..8444dd58
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcess.java
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.controller.aop;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to mark methods that require initial processing for module set tag.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ModuleInitialProcess {
+} \ No newline at end of file
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcessAspect.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcessAspect.java
new file mode 100644
index 00000000..2cecc145
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/controller/aop/ModuleInitialProcessAspect.java
@@ -0,0 +1,107 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.controller.aop;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * Aspect to handle initial processing for methods annotated with @ModuleInitialProcess.
+ */
+@Slf4j
+@Aspect
+@Component
+@RequiredArgsConstructor
+public class ModuleInitialProcessAspect {
+
+ private final ObjectMapper objectMapper;
+ private static final Map<String, Long> firstRequestTimePerModuleSetTag = new ConcurrentHashMap<>();
+
+ @Value("${delay.module-initial-processing-delay-ms:120000}")
+ private long moduleInitialProcessingDelayMs;
+
+ /**
+ * Around advice to handle methods annotated with @ModuleInitialProcess.
+ *
+ * @param proceedingJoinPoint the join point representing the method execution
+ * @param moduleInitialProcess the annotation containing the module set tag
+ * @return the result of the method execution or a ResponseEntity indicating that the service is unavailable
+ */
+ @Around("@annotation(moduleInitialProcess)")
+ public Object handleModuleInitialProcess(final ProceedingJoinPoint proceedingJoinPoint,
+ final ModuleInitialProcess moduleInitialProcess) throws Throwable {
+ log.debug("Aspect invoked for method: {}", proceedingJoinPoint.getSignature());
+ final Object moduleRequest = proceedingJoinPoint.getArgs()[1];
+ final String moduleSetTag = extractModuleSetTagFromRequest(moduleRequest);
+
+ if (isModuleSetTagEmptyOrInvalid(moduleSetTag)) {
+ log.debug("Received request with an empty or null moduleSetTag. Returning default processing.");
+ return proceedingJoinPoint.proceed();
+ }
+
+ final long firstRequestTimestamp = getFirstRequestTimestamp(moduleSetTag);
+ final long currentTimestamp = System.currentTimeMillis();
+
+ if (isInitialProcessingCompleted(currentTimestamp, firstRequestTimestamp)) {
+ log.debug("Initial processing for moduleSetTag '{}' is completed.", moduleSetTag);
+ return proceedingJoinPoint.proceed();
+ }
+
+ final long remainingProcessingTime = calculateRemainingProcessingTime(currentTimestamp, firstRequestTimestamp);
+ log.info("Initial processing for moduleSetTag '{}' is still active. Returning HTTP 503. Remaining time: {} ms.",
+ moduleSetTag, remainingProcessingTime);
+ return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();
+ }
+
+ private String extractModuleSetTagFromRequest(final Object moduleRequest) {
+ final JsonNode rootNode = objectMapper.valueToTree(moduleRequest);
+ return rootNode.path("moduleSetTag").asText(null);
+ }
+
+ private boolean isModuleSetTagEmptyOrInvalid(final String moduleSetTag) {
+ return moduleSetTag == null || moduleSetTag.trim().isEmpty();
+ }
+
+ private long getFirstRequestTimestamp(final String moduleSetTag) {
+ return firstRequestTimePerModuleSetTag
+ .computeIfAbsent(moduleSetTag, firstRequestTime -> System.currentTimeMillis());
+ }
+
+ private boolean isInitialProcessingCompleted(final long currentTimestamp, final long firstRequestTimestamp) {
+ return currentTimestamp - firstRequestTimestamp >= moduleInitialProcessingDelayMs;
+ }
+
+ private long calculateRemainingProcessingTime(final long currentTimestamp, final long firstRequestTimestamp) {
+ return moduleInitialProcessingDelayMs - (currentTimestamp - firstRequestTimestamp);
+ }
+}
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DataOperationRequest.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DataOperationRequest.java
new file mode 100644
index 00000000..41077493
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DataOperationRequest.java
@@ -0,0 +1,39 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.model.data.operational;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Setter
+@Getter
+public class DataOperationRequest {
+ private String operation;
+ private String operationId;
+ private String datastore;
+ private String options;
+ private String resourceIdentifier;
+ private List<DmiOperationCmHandle> cmHandles = new ArrayList<>();
+}
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiDataOperationRequest.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiDataOperationRequest.java
new file mode 100644
index 00000000..ae78c033
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiDataOperationRequest.java
@@ -0,0 +1,33 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.model.data.operational;
+
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+public class DmiDataOperationRequest {
+
+ private List<DataOperationRequest> operations;
+
+}
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiOperationCmHandle.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiOperationCmHandle.java
new file mode 100644
index 00000000..5cb24bc3
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/data/operational/DmiOperationCmHandle.java
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.model.data.operational;
+
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+public class DmiOperationCmHandle {
+ private String id;
+ private Map<String, String> cmHandleProperties = new HashMap<>();
+ private String moduleSetTag;
+}
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReference.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReference.java
new file mode 100644
index 00000000..5b1c30e2
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReference.java
@@ -0,0 +1,26 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.model.module;
+
+public record ModuleReference (
+ String moduleName,
+ String revision
+){ }
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReferences.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReferences.java
new file mode 100644
index 00000000..36516452
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleReferences.java
@@ -0,0 +1,27 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.model.module;
+
+import java.util.List;
+
+public record ModuleReferences(
+ List<ModuleReference> schemas
+) { }
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleResource.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleResource.java
new file mode 100644
index 00000000..f4decb8d
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/model/module/ModuleResource.java
@@ -0,0 +1,27 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.model.module;
+
+public record ModuleResource (
+ String moduleName,
+ String revision,
+ String yangSource
+) { }
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/service/YangModuleFactory.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/service/YangModuleFactory.java
new file mode 100644
index 00000000..42459222
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/service/YangModuleFactory.java
@@ -0,0 +1,161 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.annotation.PostConstruct;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.dmi.rest.stub.model.module.ModuleReference;
+import org.onap.cps.ncmp.dmi.rest.stub.model.module.ModuleReferences;
+import org.onap.cps.ncmp.dmi.rest.stub.model.module.ModuleResource;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class YangModuleFactory {
+
+ private static final int TARGET_FILE_SIZE_IN_KB = 32 * 1024;
+ private static final List<String> MODULE_SET_TAGS = generateTags();
+ private static final String DEFAULT_TAG = "tagDefault";
+ private static final int NUMBER_OF_MODULES_PER_MODULE_SET = 200;
+ private static final int NUMBER_OF_MODULES_NOT_IN_MODULE_SET = 10;
+ private static final String SERIALIZATION_ERROR = "Error serializing {}: {}";
+ private static final String MODULE_TEMPLATE = """
+ module <MODULE_NAME> {
+ yang-version 1.1;
+ namespace "org:onap:cps:test:<MODULE_NAME>";
+ prefix tree;
+ revision "<MODULE_REVISION>" {
+ description "<DESCRIPTION>";
+ }
+ container tree {
+ list branch {
+ key "name";
+ leaf name {
+ type string;
+ }
+ }
+ }
+ }
+ """;
+
+ private final ObjectMapper objectMapper;
+ private final Map<String, String> moduleReferencesJsonMap = new HashMap<>();
+ private final Map<String, String> moduleResourcesJsonMap = new HashMap<>();
+
+ @PostConstruct
+ private void initializeModuleJsonStrings() {
+ MODULE_SET_TAGS.forEach(tag -> {
+ moduleReferencesJsonMap.put(tag, createModuleReferencesJson(tag, NUMBER_OF_MODULES_PER_MODULE_SET));
+ moduleResourcesJsonMap.put(tag, createModuleResourcesJson(tag, NUMBER_OF_MODULES_PER_MODULE_SET));
+ });
+
+ // Initialize default tag
+ moduleReferencesJsonMap.put(DEFAULT_TAG,
+ createModuleReferencesJson(DEFAULT_TAG, NUMBER_OF_MODULES_NOT_IN_MODULE_SET));
+ moduleResourcesJsonMap.put(DEFAULT_TAG,
+ createModuleResourcesJson(DEFAULT_TAG, NUMBER_OF_MODULES_NOT_IN_MODULE_SET));
+ }
+
+ /**
+ * Retrieves the JSON representation of module references for the given tag.
+ *
+ * @param tag the tag identifying the set of module references
+ * @return the JSON string of module references for the specified tag, or the default tag if not found
+ */
+ public String getModuleReferencesJson(final String tag) {
+ return moduleReferencesJsonMap.getOrDefault(tag, moduleReferencesJsonMap.get(DEFAULT_TAG));
+ }
+
+ /**
+ * Retrieves the JSON representation of module resources for the given tag.
+ *
+ * @param tag the tag identifying the set of module resources
+ * @return the JSON string of module resources for the specified tag, or the default tag if not found
+ */
+ public String getModuleResourcesJson(final String tag) {
+ return moduleResourcesJsonMap.getOrDefault(tag, moduleResourcesJsonMap.get(DEFAULT_TAG));
+ }
+
+ /**
+ * Generates a list of tags from 'A' to 'E'.
+ *
+ * @return a list of tags in the format "tagX" where X is each character from 'A' to 'E'
+ */
+ public static List<String> generateTags() {
+ final List<String> tags = new ArrayList<>(5);
+ for (char currentChar = 'A'; currentChar <= 'E'; currentChar++) {
+ tags.add("tag" + currentChar);
+ }
+ return tags;
+ }
+
+ private String createModuleReferencesJson(final String tag, final int numberOfModules) {
+ final List<ModuleReference> moduleReferencesList = new ArrayList<>(numberOfModules);
+ final String moduleRevision = generateModuleRevision(tag);
+ for (int i = 0; i < numberOfModules; i++) {
+ moduleReferencesList.add(new ModuleReference("module" + i, moduleRevision));
+ }
+ return serializeToJson(new ModuleReferences(moduleReferencesList), "ModuleReferences");
+ }
+
+ private String createModuleResourcesJson(final String tag, final int numberOfModules) {
+ final List<ModuleResource> moduleResourceList = new ArrayList<>(numberOfModules);
+ final String moduleRevision = generateModuleRevision(tag);
+ for (int i = 0; i < numberOfModules; i++) {
+ final String moduleName = "module" + i;
+ final String yangSource = generateYangSource(moduleName, moduleRevision);
+ moduleResourceList.add(new ModuleResource(moduleName, moduleRevision, yangSource));
+ }
+ return serializeToJson(moduleResourceList, "ModuleResources");
+ }
+
+ private String serializeToJson(final Object objectToSerialize, final String objectType) {
+ try {
+ return objectMapper.writeValueAsString(objectToSerialize);
+ } catch (final JsonProcessingException jsonProcessingException) {
+ log.error(SERIALIZATION_ERROR, objectType, jsonProcessingException.getMessage());
+ return null;
+ }
+ }
+
+ private String generateModuleRevision(final String tag) {
+ // set tagIndex to 0 for the default tag, otherwise set it to the index of the tag in the list
+ final int tagIndex = tag.equals(DEFAULT_TAG) ? 0 : MODULE_SET_TAGS.indexOf(tag);
+ return LocalDate.of(2024, tagIndex + 1, tagIndex + 1).toString();
+ }
+
+ private static String generateYangSource(final String moduleName, final String moduleRevision) {
+ final int paddingSize = TARGET_FILE_SIZE_IN_KB - MODULE_TEMPLATE.length();
+ final String padding = "*".repeat(Math.max(0, paddingSize));
+ return MODULE_TEMPLATE.replaceAll("<MODULE_NAME>", moduleName)
+ .replace("<MODULE_REVISION>", moduleRevision)
+ .replace("<DESCRIPTION>", padding);
+ }
+} \ No newline at end of file
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/EventDateTimeFormatter.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/EventDateTimeFormatter.java
new file mode 100644
index 00000000..7b6d1ab7
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/EventDateTimeFormatter.java
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022-2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.utils;
+
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import org.apache.commons.lang3.StringUtils;
+
+public interface EventDateTimeFormatter {
+
+ String ISO_TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
+
+ DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(ISO_TIMESTAMP_PATTERN);
+
+ /**
+ * Gets current date time.
+ *
+ * @return the current date time
+ */
+ static String getCurrentIsoFormattedDateTime() {
+ return ZonedDateTime.now().format(ISO_TIMESTAMP_FORMATTER);
+ }
+
+ static OffsetDateTime toIsoOffsetDateTime(final String dateTimestampAsString) {
+ return StringUtils.isNotBlank(dateTimestampAsString)
+ ? OffsetDateTime.parse(dateTimestampAsString, ISO_TIMESTAMP_FORMATTER) : null;
+ }
+}
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ModuleResponseType.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ModuleResponseType.java
new file mode 100644
index 00000000..9c5482d1
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ModuleResponseType.java
@@ -0,0 +1,27 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.utils;
+
+public enum ModuleResponseType {
+ MODULE_REFERENCE_RESPONSE,
+ MODULE_RESOURCE_RESPONSE;
+}
+
diff --git a/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ResourceFileReaderUtil.java b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ResourceFileReaderUtil.java
new file mode 100644
index 00000000..0d2adee4
--- /dev/null
+++ b/dmi-stub/dmi-stub-service/src/main/java/org/onap/cps/ncmp/dmi/rest/stub/utils/ResourceFileReaderUtil.java
@@ -0,0 +1,51 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.dmi.rest.stub.utils;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.Resource;
+import org.springframework.util.StreamUtils;
+
+/**
+ * Common convenience methods for reading resource file content.
+ */
+@Slf4j
+public class ResourceFileReaderUtil {
+
+ /**
+ * Converts a resource file content into string.
+ *
+ * @param fileClasspath to name of the file in test/resources
+ * @return the content of the file as a String
+ * @throws IOException when there is an IO issue
+ */
+ public static String getResourceFileContent(final Resource fileClasspath) {
+ String fileContent = null;
+ try {
+ fileContent = StreamUtils.copyToString(fileClasspath.getInputStream(), StandardCharsets.UTF_8);
+ } catch (final IOException ioException) {
+ log.debug("unable to read resource file content. cause : {}", ioException.getMessage());
+ }
+ return fileContent;
+ }
+}