diff options
Diffstat (limited to 'dmi-stub/dmi-stub-service')
19 files changed, 1484 insertions, 0 deletions
diff --git a/dmi-stub/dmi-stub-service/pom.xml b/dmi-stub/dmi-stub-service/pom.xml new file mode 100644 index 00000000..171b5d90 --- /dev/null +++ b/dmi-stub/dmi-stub-service/pom.xml @@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============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========================================================= +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.cps</groupId> + <artifactId>dmi-stub</artifactId> + <version>1.6.0-SNAPSHOT</version> + </parent> + + <artifactId>dmi-stub-service</artifactId> + <packaging>jar</packaging> + + <properties> + <gmavenplus.plugin.version>4.0.1</gmavenplus.plugin.version> + <maven.compiler.plugin.version>3.13.0</maven.compiler.plugin.version> + <maven.compiler.source>17</maven.compiler.source> + <maven.compiler.target>17</maven.compiler.target> + <maven.surefire.plugin.version>3.3.1</maven.surefire.plugin.version> + </properties> + + <dependencies> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion></exclusions> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + <exclusions> + <exclusion> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-tomcat</artifactId> + </exclusion> + </exclusions> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + <dependency> + <groupId>com.googlecode.json-simple</groupId> + <artifactId>json-simple</artifactId> + <version>1.1.1</version> + <exclusions> + <exclusion> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> + <dependency> + <groupId>io.cloudevents</groupId> + <artifactId>cloudevents-kafka</artifactId> + </dependency> + <dependency> + <groupId>io.cloudevents</groupId> + <artifactId>cloudevents-spring</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.kafka</groupId> + <artifactId>spring-kafka</artifactId> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + <dependency> + <groupId>jakarta.servlet</groupId> + <artifactId>jakarta.servlet-api</artifactId> + </dependency> + + <dependency> + <groupId>org.onap.cps</groupId> + <artifactId>cps-ncmp-events</artifactId> + <version>${cps.version}</version> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-aop</artifactId> + </dependency> + <dependency> + <groupId>io.swagger.core.v3</groupId> + <artifactId>swagger-annotations</artifactId> + </dependency> + + <!-- T E S T D E P E N D E N C I E S --> + <dependency> + <groupId>org.spockframework</groupId> + <artifactId>spock-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.spockframework</groupId> + <artifactId>spock-spring</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.codehaus.groovy</groupId> + <artifactId>groovy</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.codehaus.groovy</groupId> + <artifactId>groovy-json</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven.compiler.plugin.version}</version> + <configuration> + <encoding>${project.build.sourceEncoding}</encoding> + <source>17</source> + <target>17</target> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.gmavenplus</groupId> + <artifactId>gmavenplus-plugin</artifactId> + <version>${gmavenplus.plugin.version}</version> + <executions> + <execution> + <goals> + <goal>compileTests</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>${maven.surefire.plugin.version}</version> + <configuration> + <!--suppress UnresolvedMavenProperty --> + <argLine>${surefireArgLine}</argLine> + <useFile>false</useFile> + <includes> + <include>**/*Spec.java</include> + <include>**/*Test.java</include> <!-- Just in case of having also "normal" JUnit tests --> + </includes> + </configuration> + </plugin> + </plugins> + </build> +</project>
\ 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/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; + } +} diff --git a/dmi-stub/dmi-stub-service/src/main/resources/application.yml b/dmi-stub/dmi-stub-service/src/main/resources/application.yml new file mode 100644 index 00000000..541cd6b1 --- /dev/null +++ b/dmi-stub/dmi-stub-service/src/main/resources/application.yml @@ -0,0 +1,72 @@ +# ============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========================================================= +server: + port: 8092 + jetty: + threads: + max: 25 + +rest: + api: + dmi-stub-base-path: /dmi + +spring: + main: + banner-mode: "off" + application: + name: "dmi-plugin-demo-and-csit-stub" + + kafka: + bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVER:localhost:19092} + security: + protocol: PLAINTEXT + producer: + value-serializer: io.cloudevents.kafka.CloudEventSerializer + client-id: cps-core + +management: + endpoints: + web: + exposure: + include: health + endpoint: + health: + show-details: always + # kubernetes probes: liveness and readiness + probes: + enabled: true + +app: + ncmp: + async-m2m: + topic: ${NCMP_ASYNC_M2M_TOPIC:ncmp-async-m2m} + +delay: + module-references-delay-ms: ${MODULE_REFERENCES_DELAY_MS:100} + module-resources-delay-ms: ${MODULE_RESOURCES_DELAY_MS:1000} + module-initial-processing-delay-ms: ${MODULE_INITIAL_PROCESSING_DELAY_MS:120000} + read-data-for-cm-handle-delay-ms: ${READ_DATA_FOR_CM_HANDLE_DELAY_MS:300} + write-data-for-cm-handle-delay-ms: ${WRITE_DATA_FOR_CM_HANDLE_DELAY_MS:670} + +logging: + level: + org: + springframework: + web: + filter: + CommonsRequestLoggingFilter: DEBUG diff --git a/dmi-stub/dmi-stub-service/src/main/resources/data/ietf-network-topology-sample-rfc8345.json b/dmi-stub/dmi-stub-service/src/main/resources/data/ietf-network-topology-sample-rfc8345.json new file mode 100644 index 00000000..8f9dbc22 --- /dev/null +++ b/dmi-stub/dmi-stub-service/src/main/resources/data/ietf-network-topology-sample-rfc8345.json @@ -0,0 +1,76 @@ +{ + "ietf-network:networks": { + "network": [ + { + "network-types": { + }, + "network-id": "otn-hc", + "node": [ + { + "node-id": "D1", + "termination-point": [ + { + "tp-id": "1-0-1" + }, + { + "tp-id": "1-2-1" + }, + { + "tp-id": "1-3-1" + } + ] + }, + { + "node-id": "D2", + "termination-point": [ + { + "tp-id": "2-0-1" + }, + { + "tp-id": "2-1-1" + }, + { + "tp-id": "2-3-1" + } + ] + }, + { + "node-id": "D3", + "termination-point": [ + { + "tp-id": "3-1-1" + }, + { + "tp-id": "3-2-1" + } + ] + } + ], + "ietf-network-topology:link": [ + { + "link-id": "D1,1-2-1,D2,2-1-1", + "source": { + "source-node": "D1", + "source-tp": "1-2-1" + }, + "destination": { + "dest-node": "D2", + "dest-tp": "2-1-1" + } + }, + { + "link-id": "D2,2-1-1,D1,1-2-1", + "source": { + "source-node": "D2", + "source-tp": "2-1-1" + }, + "destination": { + "dest-node": "D1", + "dest-tp": "1-2-1" + } + } + ] + } + ] + } + } diff --git a/dmi-stub/dmi-stub-service/src/main/resources/module/ietfYang-ModuleResourcesResponse.json b/dmi-stub/dmi-stub-service/src/main/resources/module/ietfYang-ModuleResourcesResponse.json new file mode 100644 index 00000000..4326733f --- /dev/null +++ b/dmi-stub/dmi-stub-service/src/main/resources/module/ietfYang-ModuleResourcesResponse.json @@ -0,0 +1,52 @@ +[ + { + "moduleName": "ietf-yang-types-1", + "revision": "2013-07-15", + "yangSource": "module ietf-yang-types-1 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-1\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-2", + "revision": "2013-07-16", + "yangSource": "module ietf-yang-types-2 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-2\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-16 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-3", + "revision": "2013-07-17", + "yangSource": "module ietf-yang-types-3 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-3\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-17 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-4", + "revision": "2013-07-18", + "yangSource": "module ietf-yang-types-4 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-4\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-18 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-5", + "revision": "2013-07-19", + "yangSource": "module ietf-yang-types-5 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-5\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-19 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-6", + "revision": "2013-07-20", + "yangSource": "module ietf-yang-types-6 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-6\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-20 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-7", + "revision": "2013-07-21", + "yangSource": "module ietf-yang-types-7 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-7\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-21 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-8", + "revision": "2013-07-22", + "yangSource": "module ietf-yang-types-8 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-8\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-22 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-9", + "revision": "2013-07-23", + "yangSource": "module ietf-yang-types-9 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-9\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-23 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + }, + { + "moduleName": "ietf-yang-types-10", + "revision": "2013-07-24", + "yangSource": "module ietf-yang-types-10 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-10\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-24 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n" + } +]
\ No newline at end of file diff --git a/dmi-stub/dmi-stub-service/src/main/resources/module/ietfYang-ModuleResponse.json b/dmi-stub/dmi-stub-service/src/main/resources/module/ietfYang-ModuleResponse.json new file mode 100644 index 00000000..2cbd8d13 --- /dev/null +++ b/dmi-stub/dmi-stub-service/src/main/resources/module/ietfYang-ModuleResponse.json @@ -0,0 +1,44 @@ +{ + "schemas": [ + { + "moduleName": "ietf-yang-types-1", + "revision": "2013-07-15" + }, + { + "moduleName": "ietf-yang-types-2", + "revision": "2013-07-16" + }, + { + "moduleName": "ietf-yang-types-3", + "revision": "2013-07-17" + }, + { + "moduleName": "ietf-yang-types-4", + "revision": "2013-07-18" + }, + { + "moduleName": "ietf-yang-types-5", + "revision": "2013-07-19" + }, + { + "moduleName": "ietf-yang-types-6", + "revision": "2013-07-20" + }, + { + "moduleName": "ietf-yang-types-7", + "revision": "2013-07-21" + }, + { + "moduleName": "ietf-yang-types-8", + "revision": "2013-07-22" + }, + { + "moduleName": "ietf-yang-types-9", + "revision": "2013-07-23" + }, + { + "moduleName": "ietf-yang-types-10", + "revision": "2013-07-24" + } + ] +}
\ No newline at end of file diff --git a/dmi-stub/dmi-stub-service/src/test/groovy/org/onap/cps/ncmp/dmi/rest/stub/utils/EventDateTimeFormatterSpec.groovy b/dmi-stub/dmi-stub-service/src/test/groovy/org/onap/cps/ncmp/dmi/rest/stub/utils/EventDateTimeFormatterSpec.groovy new file mode 100644 index 00000000..9e229127 --- /dev/null +++ b/dmi-stub/dmi-stub-service/src/test/groovy/org/onap/cps/ncmp/dmi/rest/stub/utils/EventDateTimeFormatterSpec.groovy @@ -0,0 +1,27 @@ +package org.onap.cps.ncmp.dmi.rest.stub.utils; + +import spock.lang.Specification + +import java.time.Year + +class EventDateTimeFormatterSpec extends Specification { + + def 'Get ISO formatted date and time.' () { + expect: 'iso formatted date and time starts with current year' + assert EventDateTimeFormatter.getCurrentIsoFormattedDateTime().startsWith(String.valueOf(Year.now())) + } + + def 'Convert date time from string to OffsetDateTime type.'() { + when: 'date time as a string is converted to OffsetDateTime type' + def result = EventDateTimeFormatter.toIsoOffsetDateTime('2024-05-28T18:28:02.869+0100') + then: 'the result convert back back to a string is the same as the original timestamp (except the format of timezone offset)' + assert result.toString() == '2024-05-28T18:28:02.869+01:00' + } + + def 'Convert blank string.' () { + expect: 'converting a blank string result in null' + assert EventDateTimeFormatter.toIsoOffsetDateTime(' ') == null + } + +} + |