Merge "CmSubscription validate datastore"
authorLee Anjella Macabuhay <lee.anjella.macabuhay@est.tech>
Tue, 6 Feb 2024 10:49:01 +0000 (10:49 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 6 Feb 2024 10:49:01 +0000 (10:49 +0000)
35 files changed:
cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp.yml
cps-ncmp-rest/docs/openapi/openapi.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_ResourcesResponse.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_Response.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_ResourcesResponse.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_Response.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_ResourcesResponse.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_Response.json [new file with mode: 0644]
docker-compose/docker-compose.yml
docs/_static/cps-delta-mechanism.png [new file with mode: 0644]
docs/cps-delta-endpoints.rst [new file with mode: 0644]
docs/cps-delta-feature.rst [new file with mode: 0644]
docs/modeling.rst
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy

index e33af45..0e4f7f9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada
- *  Modifications Copyright (c) 2022-2023 Nordix Foundation
+ *  Modifications Copyright (c) 2022-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.
@@ -167,7 +167,9 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
     }
 
     @Override
-    public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitionsByCmHandleId(final String cmHandle) {
+    public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitions(final String cmHandleId,
+                                                                           final String moduleName,
+                                                                           final String revision) {
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
 
index 3bee633..6b53292 100644 (file)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021-2023 Nordix Foundation
+#  Copyright (C) 2021-2024 Nordix Foundation
 #  Modifications Copyright (C) 2021 Pantheon.tech
 #  Modifications Copyright (C) 2022 Bell Canada
 #  ================================================================================
@@ -492,6 +492,22 @@ components:
       schema:
         type: string
         example: my-cm-handle
+    moduleNameInQuery:
+      name: module-name
+      in: query
+      description: Filter for a module name.This is an optional parameter
+      required: false
+      schema:
+        type: string
+        example: my-module
+    revisionInQuery:
+      name: revision
+      in: query
+      description: Filter for a module revision.This is an optional parameter and ignored when no module name is supplied
+      required: false
+      schema:
+        type: string
+        example: 2024-01-22
     dataSyncEnabled:
       name: dataSyncEnabled
       in: query
index 95ca6cc..2f90155 100755 (executable)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021-2023 Nordix Foundation
+#  Copyright (C) 2021-2024 Nordix Foundation
 #  Modifications Copyright (C) 2021 Pantheon.tech
 #  Modifications Copyright (C) 2021-2022 Bell Canada
 #  ================================================================================
@@ -289,15 +289,17 @@ fetchModuleReferencesByCmHandle:
       500:
         $ref: 'components.yaml#/components/responses/InternalServerError'
 
-fetchModuleDefinitionsByCmHandle:
+getModuleDefinitions:
   get:
-    description: Fetch all module definitions (name, revision, yang resource) for a given cm handle
     tags:
       - network-cm-proxy
-    summary: Fetch all module definitions (name, revision, yang resource) for a given cm handle
-    operationId: getModuleDefinitionsByCmHandleId
+    summary: Get module definitions
+    description: Get module definitions (module name, revision, yang resource) with options to filter on module name and revision
+    operationId: getModuleDefinitions
     parameters:
       - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
+      - $ref: 'components.yaml#/components/parameters/moduleNameInQuery'
+      - $ref: 'components.yaml#/components/parameters/revisionInQuery'
     responses:
       200:
         description: OK
index 7ceb4fe..dd6d7c8 100755 (executable)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021-2023 Nordix Foundation
+#  Copyright (C) 2021-2024 Nordix Foundation
 #  Modifications Copyright (C) 2021 Pantheon.tech
 #  Modifications Copyright (C) 2021 Bell Canada
 #  ================================================================================
@@ -44,7 +44,7 @@ paths:
     $ref: 'ncmp.yml#/fetchModuleReferencesByCmHandle'
 
   /v1/ch/{cm-handle}/modules/definitions:
-    $ref: 'ncmp.yml#/fetchModuleDefinitionsByCmHandle'
+    $ref: 'ncmp.yml#/getModuleDefinitions'
 
   /v1/ch/searches:
     $ref: 'ncmp.yml#/searchCmHandles'
index be33c6a..6ec2444 100755 (executable)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Pantheon.tech
- *  Modifications Copyright (C) 2021-2023 Nordix Foundation
+ *  Modifications Copyright (C) 2021-2024 Nordix Foundation
  *  Modifications Copyright (C) 2021 highstreet technologies GmbH
  *  Modifications Copyright (C) 2021-2022 Bell Canada
  *  ================================================================================
@@ -31,6 +31,7 @@ import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH;
 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE;
 
 import io.micrometer.core.annotation.Timed;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -59,9 +60,11 @@ import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
 import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState;
 import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
 import org.onap.cps.ncmp.rest.util.DeprecationHelper;
+import org.onap.cps.spi.model.ModuleDefinition;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -317,18 +320,32 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     }
 
     /**
-     * Return module definitions for a cm handle.
+     * Return module definitions.
      *
-     * @param cmHandleId cm-handle identifier
+     * @param cmHandleId    cm-handle identifier
+     * @param moduleName    module name
+     * @param revision      the revision of the module
      * @return list of module definitions (module name, revision, yang resource content)
      */
     @Override
-    public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitionsByCmHandleId(final String cmHandleId) {
-        final List<RestModuleDefinition> restModuleDefinitions =
-                networkCmProxyDataService.getModuleDefinitionsByCmHandleId(cmHandleId).stream()
-                        .map(ncmpRestInputMapper::toRestModuleDefinition)
-                        .collect(Collectors.toList());
-        return new ResponseEntity<>(restModuleDefinitions, HttpStatus.OK);
+    public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitions(final String cmHandleId,
+                                                                           final String moduleName,
+                                                                           final String revision) {
+        final Collection<ModuleDefinition> moduleDefinitions;
+        if (StringUtils.hasText(moduleName)) {
+            moduleDefinitions =
+                networkCmProxyDataService.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, revision);
+        } else {
+            moduleDefinitions = networkCmProxyDataService.getModuleDefinitionsByCmHandleId(cmHandleId);
+            if (StringUtils.hasText(revision)) {
+                log.warn("Ignoring revision filter as no module name is provided");
+            }
+        }
+        final List<RestModuleDefinition> response = new ArrayList<>();
+        for (final ModuleDefinition moduleDefinition: moduleDefinitions) {
+            response.add(ncmpRestInputMapper.toRestModuleDefinition(moduleDefinition));
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
     }
 
     /**
index 314a0b2..983f243 100644 (file)
@@ -2,7 +2,7 @@
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Pantheon.tech
  *  Modifications Copyright (C) 2021 highstreet technologies GmbH
- *  Modifications Copyright (C) 2021-2023 Nordix Foundation
+ *  Modifications Copyright (C) 2021-2024 Nordix Foundation
  *  Modifications Copyright (C) 2021-2022 Bell Canada.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
 
 package org.onap.cps.ncmp.rest.controller
 
-import static org.onap.cps.ncmp.api.impl.inventory.CompositeState.DataStores
-import static org.onap.cps.ncmp.api.impl.inventory.CompositeState.Operational
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.DELETE
-import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
-import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
-import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
-import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-
+import ch.qos.logback.classic.Level
+import ch.qos.logback.classic.Logger
+import ch.qos.logback.classic.spi.ILoggingEvent
+import ch.qos.logback.core.read.ListAppender
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.mapstruct.factory.Mappers
 import org.onap.cps.TestUtils
+import org.onap.cps.events.EventsPublisher
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
 import org.onap.cps.ncmp.api.NetworkCmProxyQueryService
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
@@ -50,19 +38,20 @@ import org.onap.cps.ncmp.api.impl.inventory.CompositeState
 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
-import org.onap.cps.ncmp.rest.model.DataOperationRequest
-import org.onap.cps.ncmp.rest.model.DataOperationDefinition
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.ncmp.rest.controller.handlers.NcmpCachedResourceRequestHandler
 import org.onap.cps.ncmp.rest.controller.handlers.NcmpPassthroughResourceRequestHandler
 import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
 import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
 import org.onap.cps.ncmp.rest.mapper.DataOperationRequestMapper
+import org.onap.cps.ncmp.rest.model.DataOperationDefinition
+import org.onap.cps.ncmp.rest.model.DataOperationRequest
 import org.onap.cps.ncmp.rest.util.DeprecationHelper
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.model.ModuleDefinition
 import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.utils.JsonObjectMapper
+import org.slf4j.LoggerFactory
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Value
@@ -72,10 +61,28 @@ import org.springframework.http.MediaType
 import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Shared
 import spock.lang.Specification
+
 import java.time.OffsetDateTime
 import java.time.ZoneOffset
 import java.time.format.DateTimeFormatter
 
+import static org.onap.cps.ncmp.api.impl.inventory.CompositeState.DataStores
+import static org.onap.cps.ncmp.api.impl.inventory.CompositeState.Operational
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.DELETE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
+
 @WebMvcTest(NetworkCmProxyController)
 class NetworkCmProxyControllerSpec extends Specification {
 
@@ -130,11 +137,18 @@ class NetworkCmProxyControllerSpec extends Specification {
     def NO_REQUEST_ID = null
     def TIMOUT_FOR_TEST = 1234
 
+    def logger = Spy(ListAppender<ILoggingEvent>)
+
     def setup() {
         ncmpCachedResourceRequestHandler.notificationFeatureEnabled = true
         ncmpCachedResourceRequestHandler.timeOutInMilliSeconds = TIMOUT_FOR_TEST
         ncmpPassthroughResourceRequestHandler.notificationFeatureEnabled = true
         ncmpPassthroughResourceRequestHandler.timeOutInMilliSeconds = TIMOUT_FOR_TEST
+        setupLogger()
+    }
+
+    def cleanup() {
+        ((Logger) LoggerFactory.getLogger(EventsPublisher.class)).detachAndStopAllAppenders()
     }
 
     def 'Get Resource Data from pass-through operational.'() {
@@ -516,21 +530,46 @@ class NetworkCmProxyControllerSpec extends Specification {
             ':passthrough-running'     | 'passthrough-running'
     }
 
-    def 'Get module definitions based on cmHandleId.'() {
-        when: 'get module definition request is performed'
+    def 'Getting module definitions for a module'() {
+        when: 'get module definition request is performed with module name'
             def response = mvc.perform(
-                    get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions"))
-                    .andReturn().response
-        then: 'ncmp service method to get module definitions is called'
-            mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleId('some-cmhandle')
-                    >> [new ModuleDefinition('sampleModuleName', '2021-10-03',
-                    'module sampleModuleName{ sample module content }')]
+                get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions?module-name=sampleModuleName"))
+                .andReturn().response
+        then: 'ncmp service method is invoked with correct parameters'
+            mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleAndModule('some-cmhandle', 'sampleModuleName', _)
+                >> [new ModuleDefinition('sampleModuleName', '2021-10-03',
+                'module sampleModuleName{ sample module content }')]
         and: 'response contains an array with the module name, revision and content'
             response.getContentAsString() == '[{"moduleName":"sampleModuleName","revision":"2021-10-03","content":"module sampleModuleName{ sample module content }"}]'
         and: 'response returns an OK http code'
             response.status == HttpStatus.OK.value()
     }
 
+    def 'Getting module definitions filtering on #scenario'() {
+        when: 'get module definition request is performed'
+            def response = mvc.perform(
+                get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions?module-name=" + moduleName + "&revision=" + revision))
+                .andReturn().response
+        then: 'ncmp service method to get definitions by cm handle is invoked when needed'
+            numberOfCallsToByCmHandleId * mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleId('some-cmhandle') >> []
+        and: 'ncmp service method to get definitions by module is invoked when needed'
+            numberOfCallsToByModule * mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleAndModule('some-cmhandle', moduleName, revision) >> []
+        and: 'response returns an OK http code'
+            response.status == HttpStatus.OK.value()
+        and: 'the correct message is logged when needed'
+            if (expectLogWarning) {
+                def lastLoggingEvent = logger.list[0]
+                assert lastLoggingEvent.level == Level.WARN
+                assert lastLoggingEvent.formattedMessage.contains('Ignoring revision')
+            }
+        where: 'following parameters are used'
+            scenario                   | moduleName    | revision        || numberOfCallsToByCmHandleId | numberOfCallsToByModule | expectLogWarning
+            'module name'              | 'some-module' | ''              || 0                           | 1                       | false
+            'module name and revision' | 'some-module' | 'some-revision' || 0                           | 1                       | false
+            'no filtering'             | ''            | ''              || 1                           | 0                       | false
+            'only revision'            | ''            | 'some-revision' || 1                           | 0                       | true
+    }
+
     def 'Set the data sync enabled based on the cm handle id and the data sync flag is #scenario'() {
         when: 'the set data sync enabled request is invoked'
             def response = mvc.perform(
@@ -667,5 +706,12 @@ class NetworkCmProxyControllerSpec extends Specification {
         return dataOperationDefinition
     }
 
+    def setupLogger() {
+        def setupLogger = ((Logger) LoggerFactory.getLogger(NetworkCmProxyController.class))
+        setupLogger.setLevel(Level.DEBUG)
+        setupLogger.addAppender(logger)
+        logger.start()
+    }
+
 }
 
index 0c12875..0c84748 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 highstreet technologies GmbH
- *  Modifications Copyright (C) 2021-2023 Nordix Foundation
+ *  Modifications Copyright (C) 2021-2024 Nordix Foundation
  *  Modifications Copyright (C) 2021 Pantheon.tech
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
@@ -125,6 +125,18 @@ public interface NetworkCmProxyDataService {
      */
     Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(String cmHandleId);
 
+    /**
+     * Get module definitions for the given parameters.
+     *
+     * @param cmHandleId        cm-handle identifier
+     * @param moduleName        module name
+     * @param moduleRevision    the revision of the module
+     * @return list of module definitions (module name, revision, yang resource content)
+     */
+    Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(String cmHandleId,
+                                                                         String moduleName,
+                                                                         String moduleRevision);
+
     /**
      * Query cm handle details by cm handle's name.
      *
index 3fa0504..05b83b9 100755 (executable)
@@ -107,6 +107,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
     private final TrustLevelManager trustLevelManager;
     private final CmHandleIdMapper cmHandleIdMapper;
+    private final Map<String, Collection<ModuleReference>> moduleSetTagCache;
 
     @Override
     public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
@@ -192,6 +193,13 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         return inventoryPersistence.getModuleDefinitionsByCmHandleId(cmHandleId);
     }
 
+    @Override
+    public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(final String cmHandleId,
+                                                                                final String moduleName,
+                                                                                final String moduleRevision) {
+        return inventoryPersistence.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, moduleRevision);
+    }
+
     /**
      * Retrieve cm handles with details for the given query parameters.
      *
@@ -346,13 +354,16 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
             new ArrayList<>(tobeRemovedCmHandles.size());
         final Collection<YangModelCmHandle> yangModelCmHandles =
             inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles);
-
+        final Set<String> moduleSetTags = yangModelCmHandles.stream().map(YangModelCmHandle::getModuleSetTag)
+                .collect(Collectors.toSet());
         updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING);
 
         final Set<String> notDeletedCmHandles = new HashSet<>();
         for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandles, DELETE_BATCH_SIZE)) {
             try {
                 batchDeleteCmHandlesFromDbAndModuleSyncMap(tobeRemovedCmHandleBatch);
+                moduleSetTags.forEach(moduleSetTagCache::remove);
+                log.debug("Removed module set tags: {}", moduleSetTags);
                 tobeRemovedCmHandleBatch.forEach(cmHandleId ->
                     cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)));
 
index 9024eac..dcd0368 100644 (file)
@@ -79,6 +79,18 @@ public interface InventoryPersistence extends NcmpPersistence {
      */
     Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(String cmHandleId);
 
+    /**
+     * Method to return module definitions for the given parameters.
+     *
+     * @param cmHandleId        cm-handle identifier
+     * @param moduleName        module name
+     * @param moduleRevision    the revision of the module
+     * @return list of module definitions (module name, revision, yang resource content)
+     */
+    Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(String cmHandleId,
+                                                                         String moduleName,
+                                                                         String moduleRevision);
+
     /**
      * Method to return module references by cmHandleId.
      *
index 33d6e9a..3b70786 100644 (file)
@@ -127,6 +127,15 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
         return cpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
     }
 
+    @Override
+    public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(final String cmHandleId,
+                                                                                final String moduleName,
+                                                                                final String moduleRevision) {
+        cpsValidator.validateNameCharacters(cmHandleId, moduleName);
+        return cpsModuleService.getModuleDefinitionsByAnchorAndModule(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
+                    cmHandleId, moduleName, moduleRevision);
+    }
+
     @Override
     public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) {
         cpsValidator.validateNameCharacters(cmHandleId);
index 750be2d..22bbc73 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-2024 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -68,6 +68,10 @@ public class ModuleOperationsUtils {
     private static final String LOCK_REASON_DETAILS_MSG_FORMAT = UPGRADE_FORMAT + " Attempt #%d failed: %s";
     private static final Pattern retryAttemptPattern = Pattern.compile("Attempt #(\\d+) failed:.+");
     private static final Pattern moduleSetTagPattern = Pattern.compile("Upgrade to ModuleSetTag: (\\S+)");
+    private static final String CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE = """
+            //lock-reason[@reason="MODULE_SYNC_FAILED"
+            or @reason="MODULE_UPGRADE"
+            or @reason="MODULE_UPGRADE_FAILED"]""";
 
     /**
      * Query data nodes for cm handles with an "ADVISED" cm handle state.
@@ -110,8 +114,7 @@ public class ModuleOperationsUtils {
      */
     public List<YangModelCmHandle> getCmHandlesThatFailedModelSyncOrUpgrade() {
         final List<DataNode> lockedCmHandlesAsDataNodeList
-                = cmHandleQueries.queryCmHandleAncestorsByCpsPath(
-                "//lock-reason[@reason=\"MODULE_SYNC_FAILED\" or @reason=\"MODULE_UPGRADE\"]",
+                = cmHandleQueries.queryCmHandleAncestorsByCpsPath(CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE,
                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
         return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList);
     }
index b21a2f1..72d3226 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-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.
@@ -91,6 +91,9 @@ public class ModuleSyncService {
                                 existingAnchorName, inUpgrade);
                 updateModuleSetTagCache(moduleSetTag, moduleReferencesFromExistingCmHandle);
             } else {
+                if (inUpgrade) {
+                    deleteSchemaSetIfExists(cmHandleId);
+                }
                 final Collection<ModuleReference> allModuleReferencesFromCmHandle
                         = syncAndCreateSchemaSet(yangModelCmHandle);
                 updateModuleSetTagCache(moduleSetTag, allModuleReferencesFromCmHandle);
index 8175fb5..a88adbd 100644 (file)
@@ -73,7 +73,13 @@ public class CmHandleIdMapper {
 
     public void removeMapping(final String cmHandleId) {
         final String alternateId = alternateIdPerCmHandleId.remove(cmHandleId);
-        cmHandleIdPerAlternateId.remove(alternateId);
+        removeAlternateIdWithValidation(alternateId);
+    }
+
+    private void removeAlternateIdWithValidation(final String alternateId) {
+        if (alternateId != null) {
+            cmHandleIdPerAlternateId.remove(alternateId);
+        }
     }
 
     private void initializeCache() {
index 59bf942..c7ac8ab 100644 (file)
 
 package org.onap.cps.ncmp.api.impl
 
-import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
-import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
-import org.onap.cps.ncmp.api.models.UpgradedCmHandles
-
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 
+import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
+import org.onap.cps.ncmp.api.models.UpgradedCmHandles
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.hazelcast.map.IMap
 import org.onap.cps.api.CpsDataService
@@ -52,14 +51,11 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.exceptions.DataValidationException
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
 import org.onap.cps.utils.JsonObjectMapper
-import spock.lang.Shared
 import spock.lang.Specification
 
 class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
-    @Shared
     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')
-
     def mockCpsModuleService = Mock(CpsModuleService)
     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
     def mockDmiDataOperations = Mock(DmiDataOperations)
@@ -74,6 +70,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     def mockTrustLevelManager = Mock(TrustLevelManager)
     def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
     def objectUnderTest = getObjectUnderTest()
+    def mockModuleSetTagCache = [:]
 
     def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() {
         given: 'a registration with operations of all types'
@@ -91,7 +88,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         then: 'cm-handles are removed first'
             1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_)
         and: 'de-registered cm handle entry is removed from in progress map'
-            1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
+            2 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
         then: 'cm-handles are created'
             1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_)
         then: 'cm-handles are updated'
@@ -448,7 +445,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
                 mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries,
                 stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService,
-                mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin as Map<String, TrustLevel>, mockTrustLevelManager, mockCmHandleIdMapper))
+                mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockCmHandleIdMapper, mockModuleSetTagCache))
     }
 
     def addPersistedYangModelCmHandles(ids) {
index c1af902..c835056 100644 (file)
@@ -23,8 +23,6 @@
 
 package org.onap.cps.ncmp.api.impl
 
-import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
-
 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
@@ -34,6 +32,7 @@ import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RU
 import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
 
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
 import com.hazelcast.map.IMap
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
@@ -82,6 +81,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
     def mockTrustLevelManager = Mock(TrustLevelManager)
     def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
+    def mockModuleSetTagCache = [:]
 
     def NO_TOPIC = null
     def NO_REQUEST_ID = null
@@ -102,8 +102,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             stubModuleSyncStartedOnCmHandles,
             stubTrustLevelPerDmiPlugin,
             mockTrustLevelManager,
-            mockCmHandleIdMapper
-    )
+            mockCmHandleIdMapper,
+            mockModuleSetTagCache)
 
     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
 
@@ -298,10 +298,17 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             assert result == ['cm-handle-id-1']
     }
 
-    def 'Getting module definitions.'() {
-        when: 'get module definitions method is called with a valid cm handle ID'
+    def 'Getting module definitions by module'() {
+        when: 'get module definitions is performed with module name'
+            objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
+        then: 'ncmp inventory persistence service is invoked once with correct parameters'
+            1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
+    }
+
+    def 'Getting module definitions by cm handle id'() {
+        when: 'get module definitions is performed with cm handle id'
             objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
-        then: 'CPS module services is invoked once'
+        then: 'ncmp inventory persistence service is invoked once with correct parameter'
             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
     }
 
index cb2f3fd..a3b923f 100644 (file)
@@ -184,11 +184,23 @@ class InventoryPersistenceImplSpec extends Specification {
             'DELETING'  | CmHandleState.DELETING || ['/dmi-registry/cm-handles[@id=\'Some-Cm-Handle1\']':'{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle2\']':'{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}']
     }
 
-    def 'Get module definitions'() {
-        given: 'cps module service returns a collection of module definitions'
+    def 'Getting module definitions by module'() {
+        given: 'cps module service returns module definition for module name'
+            def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
+            mockCpsModuleService.getModuleDefinitionsByAnchorAndModule(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id', 'some-module', '2024-01-25') >> moduleDefinitions
+        when: 'get module definitions is invoked with module name'
+            def result = objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cmHandle-Id', 'some-module', '2024-01-25')
+        then: 'returned result are the same module definitions as returned from module service'
+            assert result == moduleDefinitions
+        and: 'cm handle id and module name validated'
+            1 * mockCpsValidator.validateNameCharacters('some-cmHandle-Id', 'some-module')
+    }
+
+    def 'Getting module definitions with cm handle id'() {
+        given: 'cps module service returns module definitions for cm handle id'
             def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
             mockCpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id') >> moduleDefinitions
-        when: 'get module definitions by cmHandle is invoked'
+        when: 'get module definitions is invoked with cm handle id'
             def result = objectUnderTest.getModuleDefinitionsByCmHandleId('some-cmHandle-Id')
         then: 'the returned result are the same module definitions as returned from the module service'
             assert result == moduleDefinitions
index 8f3d8d9..827a548 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-2024 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -128,10 +128,9 @@ class ModuleOperationsUtilsSpec extends Specification{
             'empty module set tag' | ''                 || 'not-specified'
     }
 
-    def 'Get all locked Cm-Handle where Lock Reason is MODULE_SYNC_FAILED cm handle #scenario'() {
+    def 'Get all locked cm-Handles where lock reasons are model sync failed or upgrade'() {
         given: 'the cps (persistence service) returns a collection of data nodes'
-            mockCmHandleQueries.queryCmHandleAncestorsByCpsPath(
-                    '//lock-reason[@reason="MODULE_SYNC_FAILED" or @reason="MODULE_UPGRADE"]',
+            mockCmHandleQueries.queryCmHandleAncestorsByCpsPath(ModuleOperationsUtils.CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE,
                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode]
         when: 'get locked Misbehaving cm handle is called'
             def result = objectUnderTest.getCmHandlesThatFailedModelSyncOrUpgrade()
index b115f9a..1e42702 100755 (executable)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2020-2023 Nordix Foundation
+ *  Copyright (C) 2020-2024 Nordix Foundation
  *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  Modifications Copyright (C) 2021 Pantheon.tech
  *  Modifications Copyright (C) 2022 TechMahindra Ltd.
@@ -32,6 +32,7 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -79,6 +80,8 @@ import org.springframework.stereotype.Component;
 public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceService {
 
     private static final String YANG_RESOURCE_CHECKSUM_CONSTRAINT_NAME = "yang_resource_checksum_key";
+    private static final String NO_MODULE_NAME_FILTER = null;
+    private static final String NO_MODULE_REVISION = null;
     private static final Pattern CHECKSUM_EXCEPTION_PATTERN = Pattern.compile(".*\\(checksum\\)=\\((\\w+)\\).*");
     private static final Pattern RFC6020_RECOMMENDED_FILENAME_PATTERN = Pattern
             .compile("([\\w-]+)@(\\d{4}-\\d{2}-\\d{2})(?:\\.yang)?", Pattern.CASE_INSENSITIVE);
@@ -122,10 +125,29 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
     public Collection<ModuleDefinition> getYangResourceDefinitions(final String dataspaceName,
                                                                    final String anchorName) {
         final Set<YangResourceEntity> yangResourceEntities =
-                yangResourceRepository
-                        .findAllModuleDefinitionsByDataspaceAndAnchor(dataspaceName, anchorName);
-        return yangResourceEntities.stream().map(CpsModulePersistenceServiceImpl::toModuleDefinition)
-                .collect(Collectors.toList());
+                yangResourceRepository.findAllModuleDefinitionsByDataspaceAndAnchorAndModule(dataspaceName, anchorName,
+                    NO_MODULE_NAME_FILTER, NO_MODULE_REVISION);
+        return convertYangResourceEntityToModuleDefinition(yangResourceEntities);
+    }
+
+    @Override
+    public Collection<ModuleDefinition> getYangResourceDefinitionsByAnchorAndModule(final String dataspaceName,
+                                                                                    final String anchorName,
+                                                                                    final String moduleName,
+                                                                                    final String moduleRevision) {
+        final Set<YangResourceEntity> yangResourceEntities =
+            yangResourceRepository.findAllModuleDefinitionsByDataspaceAndAnchorAndModule(dataspaceName, anchorName,
+                moduleName, moduleRevision);
+        return convertYangResourceEntityToModuleDefinition(yangResourceEntities);
+    }
+
+    private List<ModuleDefinition> convertYangResourceEntityToModuleDefinition(final Set<YangResourceEntity>
+                                                                                   yangResourceEntities) {
+        final List<ModuleDefinition> resultModuleDefinitions = new ArrayList<>(yangResourceEntities.size());
+        for (final YangResourceEntity yangResourceEntity: yangResourceEntities) {
+            resultModuleDefinitions.add(toModuleDefinition(yangResourceEntity));
+        }
+        return resultModuleDefinitions;
     }
 
     @Override
index 3fec462..b37f635 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Pantheon.tech
- *  Modifications Copyright (C) 2021-2023 Nordix Foundation
+ *  Modifications Copyright (C) 2021-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.
@@ -84,9 +84,12 @@ public interface YangResourceRepository extends JpaRepository<YangResourceEntity
             WHERE
                     dataspace.name = :dataspaceName
                 AND anchor.name = :anchorName
+                AND (:moduleName IS NULL OR yang_resource.module_name = :moduleName)
+                AND (:revision IS NULL OR yang_resource.revision = :revision)
             """, nativeQuery = true)
-    Set<YangResourceEntity> findAllModuleDefinitionsByDataspaceAndAnchor(
-            @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName);
+    Set<YangResourceEntity> findAllModuleDefinitionsByDataspaceAndAnchorAndModule(
+            @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName,
+            @Param("moduleName") String moduleName, @Param("revision") String revision);
 
     @Query(value = """
             SELECT DISTINCT
index 2928464..bdd3614 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2020-2023 Nordix Foundation
+ *  Copyright (C) 2020-2024 Nordix Foundation
  *  Modifications Copyright (C) 2020-2021 Pantheon.tech
  *  Modifications Copyright (C) 2022 TechMahindra Ltd.
  *  ================================================================================
@@ -133,6 +133,18 @@ public interface CpsModuleService {
      */
     Collection<ModuleDefinition> getModuleDefinitionsByAnchorName(String dataspaceName, String anchorName);
 
+    /**
+     * Retrieve module definitions for the given parameters.
+     *
+     * @param dataspaceName     dataspace name
+     * @param anchorName        anchor name
+     * @param moduleName        module name
+     * @param moduleRevision    the revision of the module
+     * @return a collection of module definitions (moduleName, revision, yang resource content)
+     */
+    Collection<ModuleDefinition> getModuleDefinitionsByAnchorAndModule(String dataspaceName, String anchorName,
+                                                                  String moduleName, String moduleRevision);
+
     /**
      * Identify previously unknown Yang Resource module references.
      * The system will ignore the namespace of all module references.
index 61a4e62..0f9eb3c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2020-2023 Nordix Foundation
+ *  Copyright (C) 2020-2024 Nordix Foundation
  *  Modifications Copyright (C) 2020-2021 Pantheon.tech
  *  Modifications Copyright (C) 2022 Bell Canada
  *  Modifications Copyright (C) 2022 TechMahindra Ltd
@@ -155,6 +155,15 @@ public class CpsModuleServiceImpl implements CpsModuleService {
         return cpsModulePersistenceService.getYangResourceDefinitions(dataspaceName, anchorName);
     }
 
+    @Override
+    public Collection<ModuleDefinition> getModuleDefinitionsByAnchorAndModule(final String dataspaceName,
+                                                                              final String anchorName,
+                                                                              final String moduleName,
+                                                                              final String moduleRevision) {
+        return cpsModulePersistenceService.getYangResourceDefinitionsByAnchorAndModule(dataspaceName, anchorName,
+            moduleName, moduleRevision);
+    }
+
     @Override
     public Collection<ModuleReference> identifyNewModuleReferences(
         final Collection<ModuleReference> moduleReferencesToCheck) {
index aaca2ee..eeaaa47 100755 (executable)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2020-2023 Nordix Foundation
+ *  Copyright (C) 2020-2024 Nordix Foundation
  *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  Modifications Copyright (C) 2022 TechMahindra Ltd.
  *  ================================================================================
@@ -125,6 +125,18 @@ public interface CpsModulePersistenceService {
      */
     Collection<ModuleDefinition> getYangResourceDefinitions(String dataspaceName, String anchorName);
 
+    /**
+     * Get YANG resource definitions for the given parameters.
+     *
+     * @param dataspaceName  dataspace name
+     * @param anchorName     anchor name
+     * @param moduleName     module name
+     * @param moduleRevision the revision of the module
+     * @return a collection of module definitions (moduleName, revision and yang resource content)
+     */
+    Collection<ModuleDefinition> getYangResourceDefinitionsByAnchorAndModule(String dataspaceName, String anchorName,
+                                                                             String moduleName, String moduleRevision);
+
     /**
      * Remove unused Yang Resource Modules.
      */
index d909e27..0bad0de 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2020-2023 Nordix Foundation
+ *  Copyright (C) 2020-2024 Nordix Foundation
  *  Modifications Copyright (C) 2020-2021 Pantheon.tech
  *  Modifications Copyright (C) 2020-2022 Bell Canada.
  *  Modifications Copyright (C) 2022 TechMahindra Ltd.
@@ -238,15 +238,25 @@ class CpsModuleServiceImplSpec extends Specification {
             1 * mockCpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck)
     }
 
-    def 'Getting module definitions.'() {
-        given: 'the module persistence service returns a collection of module definitions'
+    def 'Getting module definitions with module name'() {
+        given: 'module persistence service returns module definitions for module name'
+            def moduleDefinitionsFromPersistenceService = [ new ModuleDefinition('name', 'revision', 'content' ) ]
+            mockCpsModulePersistenceService.getYangResourceDefinitionsByAnchorAndModule('some-dataspace-name', 'some-anchor-name', 'some-module', '2024-01-01')  >> moduleDefinitionsFromPersistenceService
+        when: 'get module definitions method is called with anchor and module name'
+            def result = objectUnderTest.getModuleDefinitionsByAnchorAndModule('some-dataspace-name', 'some-anchor-name', 'some-module', '2024-01-01')
+        then: 'the result is the same collection returned by the persistence service'
+            assert result == moduleDefinitionsFromPersistenceService
+    }
+
+    def 'Getting module definitions with anchor name'() {
+        given: 'the module persistence service returns module definitions for cm handle id'
             def moduleDefinitionsFromPersistenceService = [ new ModuleDefinition('name', 'revision', 'content' ) ]
             mockCpsModulePersistenceService.getYangResourceDefinitions('some-dataspace-name', 'some-anchor-name')  >> moduleDefinitionsFromPersistenceService
         when: 'get module definitions method is called with a valid dataspace and anchor name'
             def result = objectUnderTest.getModuleDefinitionsByAnchorName('some-dataspace-name', 'some-anchor-name')
         then: 'the result is the same collection returned by the persistence service'
             assert result == moduleDefinitionsFromPersistenceService
-        and: 'the CpsValidator is called on the dataspaceName and schemaSetName'
+        and: 'cps validator is called on the dataspace and anchor name'
             1 * mockCpsValidator.validateNameCharacters('some-dataspace-name', 'some-anchor-name')
     }
 
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_ResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_ResourcesResponse.json
new file mode 100644 (file)
index 0000000..5d71391
--- /dev/null
@@ -0,0 +1,12 @@
+[
+       {
+               "moduleName": "M1",
+               "revision": "2024-01-01",
+               "yangSource": "module M1 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M1\";\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 2024-01-01 {\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": "M2",
+               "revision": "2024-01-02",
+               "yangSource": "module M2 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M2\";\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 2024-01-02 {\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-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_Response.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_Response.json
new file mode 100644 (file)
index 0000000..9f20564
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       "schemas": [
+               {
+                       "moduleName": "M1",
+                       "revision": "2024-01-01"
+               },
+               {
+                       "moduleName": "M2",
+                       "revision": "2024-01-02"
+               }
+       ]
+}
\ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_ResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_ResourcesResponse.json
new file mode 100644 (file)
index 0000000..ef9b85f
--- /dev/null
@@ -0,0 +1,12 @@
+[
+       {
+               "moduleName": "M1",
+               "revision": "2024-01-01",
+               "yangSource": "module M1 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M1\";\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 2024-01-01 {\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": "M3",
+               "revision": "2024-01-03",
+               "yangSource": "module M3 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M3\";\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 2024-01-03 {\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-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_Response.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_Response.json
new file mode 100644 (file)
index 0000000..513c749
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       "schemas": [
+               {
+                       "moduleName": "M1",
+                       "revision": "2024-01-01"
+               },
+               {
+                       "moduleName": "M3",
+                       "revision": "2024-01-03"
+               }
+       ]
+}
\ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_ResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_ResourcesResponse.json
new file mode 100644 (file)
index 0000000..5d71391
--- /dev/null
@@ -0,0 +1,12 @@
+[
+       {
+               "moduleName": "M1",
+               "revision": "2024-01-01",
+               "yangSource": "module M1 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M1\";\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 2024-01-01 {\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": "M2",
+               "revision": "2024-01-02",
+               "yangSource": "module M2 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M2\";\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 2024-01-02 {\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-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_Response.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_Response.json
new file mode 100644 (file)
index 0000000..9f20564
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       "schemas": [
+               {
+                       "moduleName": "M1",
+                       "revision": "2024-01-01"
+               },
+               {
+                       "moduleName": "M2",
+                       "revision": "2024-01-02"
+               }
+       ]
+}
\ No newline at end of file
index 11d39b5..906945d 100644 (file)
@@ -1,7 +1,7 @@
 # ============LICENSE_START=======================================================
 # Copyright (c) 2020 Pantheon.tech.
 # Modifications Copyright (C) 2021 Bell Canada.
-# Modifications Copyright (C) 2022-2023 Nordix Foundation.
+# Modifications Copyright (C) 2022-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.
@@ -31,6 +31,14 @@ services:
       POSTGRES_DB: cpsdb
       POSTGRES_USER: ${DB_USERNAME:-cps}
       POSTGRES_PASSWORD: ${DB_PASSWORD:-cps}
+    deploy:
+      resources:
+        reservations:
+          cpus: '1'
+          memory: 1G
+        limits:
+          cpus: '6'
+          memory: 3G
 
   cps-and-ncmp:
     container_name: cps-and-ncmp
@@ -55,6 +63,14 @@ services:
     restart: unless-stopped
     depends_on:
       - dbpostgresql
+    deploy:
+      resources:
+        reservations:
+          cpus: '2'
+          memory: 2G
+        limits:
+          cpus: '3'
+          memory: 3G
 
   ### if kafka is not required comment out zookeeper and kafka ###
   zookeeper:
diff --git a/docs/_static/cps-delta-mechanism.png b/docs/_static/cps-delta-mechanism.png
new file mode 100644 (file)
index 0000000..ab78ad9
Binary files /dev/null and b/docs/_static/cps-delta-mechanism.png differ
diff --git a/docs/cps-delta-endpoints.rst b/docs/cps-delta-endpoints.rst
new file mode 100644 (file)
index 0000000..b2e4e60
--- /dev/null
@@ -0,0 +1,66 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. Copyright (C) 2021 Pantheon.tech
+.. Copyright (C) 2024 TechMahindra Ltd.
+.. _cpsDeltaEndpoints:
+
+.. toctree::
+   :maxdepth: 1
+
+CPS Delta Endpoints
+###################
+
+The CPS Delta feature provides 1 endpoint:
+
+- /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/delta
+
+Description
+-----------
+The following is a Get endpoint, which allows the user to find the delta between configurations stored under two anchors within the same dataspace.
+
+Path Parameters
+---------------
+The endpoint takes 2 path parameters as input:
+    - **dataspace-name:** name of dataspace where the 2 anchors to be used for delta generation are stored.
+    - **anchor-name:** the source anchor name, the data under this anchor will be the reference data for delta report generation
+
+Query Parameters
+----------------
+The endpoint takes 3 query parameters as input:
+    - **target-anchor-name:** the data retrieved from target anchor gets compared against the data retrieved from source anchor
+    - **xpath:** the xpath to a particular data node, Example: /bookstore/categories[@code='1']
+    - **descendants:** specifies the number of descendants to query.
+
+Sample Delta Report
+-------------------
+
+.. code-block:: json
+
+    [
+      {
+        "action": "ADD",
+        "xpath": "/bookstore/categories/[@code=3]",
+        "target-data": {
+          "code": "3,",
+          "name": "kidz"
+        }
+      },
+      {
+        "action": "REMOVE",
+        "xpath": "/bookstore/categories/[@code=1]",
+        "source-data": {
+          "code": "1,",
+          "name": "Fiction"
+        }
+      },
+      {
+        "action": "UPDATE",
+        "xpath": "/bookstore/categories/[@code=2]",
+        "source-data": {
+          "name": "Funny"
+        },
+        "target-data": {
+          "name": "Comic"
+        }
+      }
+    ]
\ No newline at end of file
diff --git a/docs/cps-delta-feature.rst b/docs/cps-delta-feature.rst
new file mode 100644 (file)
index 0000000..f3a2f94
--- /dev/null
@@ -0,0 +1,44 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. Copyright (C) 2021 Pantheon.tech
+.. Copyright (C) 2024 TechMahindra Ltd.
+.. _cpsDelta:
+
+.. toctree::
+   :maxdepth: 1
+
+CPS Delta Feature
+#################
+
+- The concept of CPS Delta Feature is to have the ability to find the delta or difference between two configurations stored in CPS DB.
+
+- The Delta feature brings a new functionality:
+
+    - Ability to find the delta between the configurations stored in two anchors within the same dataspace.
+
+The calculated differences can then be used to generate a Delta Report which can be sent over the Kafka Notification Service to the user.
+
+Delta Report Format
+-------------------
+
+The Delta Report is based on the RFC 9144, which defines a set of parameters to be included in the Delta Report. In CPS only the relevant parameters defined in RFC 9144 are used. These include:
+    - **action:** This parameter defines how the data nodes changed between two configurations. If data nodes are added, deleted or modified then the 'action' parameter in the delta report will be set to ADD, DELETE or UPDATE respectively for each data node.
+    - **xpath:** This parameter will provide the xpath of each data node that has been either added, deleted or modified.
+    - **source-data:** this parameter is added to the delta report when a data node is deleted or updated, this represents the source/reference data against which the delta is being generated. In case of newly added data node this field is not included in the delta report.
+    - **target-data:** this parameter is added to the delta report when a data node is added or updated, this represents the data values that are being compared to the source data. In case of a data node being deleted this field is not included in the delta report.
+
+**Note.** In case of an existing data node being modified, both the source-data and target-data fields are present in the delta report.
+
+Mechanism for Delta generation
+------------------------------
+
+.. image:: _static/cps-delta-mechanism.png
+   :alt: Mechanism for Delta generation
+
+Endpoints provided as part of Delta Feature
+-------------------------------------------
+
+.. toctree::
+   :maxdepth: 1
+
+   cps-delta-endpoints.rst
\ No newline at end of file
index ceaaefd..7ebf6fe 100644 (file)
@@ -63,6 +63,14 @@ Querying
    xpath.rst
    cps-path.rst
 
+Additional information on CPS-Core Interfaces
+---------------------------------------------
+
+.. toctree::
+   :maxdepth: 1
+
+   cps-delta-feature.rst
+
 .. Below Label is used by documentation for other CPS components to link here, do not remove even if it gives a warning
 .. _cps_ncmp_modelling:
 
index cf0e0b5..8029ae0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
+ *  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.
@@ -21,7 +21,6 @@
 package org.onap.cps.integration.functional
 
 import org.onap.cps.api.CpsModuleService
-import org.onap.cps.api.impl.CpsModuleServiceImpl
 import org.onap.cps.integration.base.FunctionalSpecBase
 import org.onap.cps.spi.CascadeDeleteAllowed
 import org.onap.cps.spi.exceptions.AlreadyDefinedException
@@ -153,6 +152,17 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
             result == [new ModuleDefinition('stores','2020-09-15','')]
     }
 
+    def 'Retrieving module definitions: #scenarios'() {
+        when: 'module definitions for module name are retrieved'
+            def result = objectUnderTest.getModuleDefinitionsByAnchorAndModule(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, moduleName, moduleRevision)
+        then: 'the correct module definitions are returned'
+            result == [new ModuleDefinition('stores','2020-09-15','')]
+        where: 'following parameters are used'
+            scenarios               | moduleName | moduleRevision
+            'both specified'        | 'stores'   | '2020-09-15'
+            'module name specified' | 'stores'   | null
+    }
+
     def 'Retrieving yang resource module references by anchor.'() {
         when: 'the yang resource module references for an anchor are retrieved'
             def result = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1)