Merge "Define Initial Data Sync Enabled Flag and state"
authorJoseph Keenan <joseph.keenan@est.tech>
Thu, 7 Jul 2022 15:32:12 +0000 (15:32 +0000)
committerGerrit Code Review <gerrit@onap.org>
Thu, 7 Jul 2022 15:32:12 +0000 (15:32 +0000)
20 files changed:
.gitignore
INFO.yaml
cps-dependencies/pom.xml
cps-ncmp-service/pom.xml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.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/NetworkCmProxyDataServicePropertyHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfig.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/constants/DmiRegistryConstants.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
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/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfigSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy [new file with mode: 0644]

index 624bc1a..a377967 100755 (executable)
@@ -4,6 +4,7 @@
 *.log
 *.log.zip
 
+cps-ncmp-rest-stub/dependency-reduced-pom.xml
 cps-application/archunit_store
 cps-ri/src/main/resources/changelog/db/changes/data/dmi/generated-csv/generated_yang_resource_*
 target/
index 610fd0e..d1a6a49 100755 (executable)
--- a/INFO.yaml
+++ b/INFO.yaml
@@ -1,5 +1,5 @@
 # ============LICENSE_START=======================================================
-# Copyright (C) 2021 Nordix Foundation.
+# Copyright (C) 2021-2022 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -52,11 +52,6 @@ committers:
       company: 'Ericsson Software Technology'
       id: 'toinesiebelink'
       timezone: 'Europe/Dublin'
-    - name: 'Bruno Sakoto'
-      email: 'bruno.sakoto@bell.ca'
-      company: 'Bell Canada'
-      id: 'brusak'
-      timezone: 'America/Toronto'
     - name: 'Aditya Puthuparambil'
       email: 'aditya.puthuparambil@bell.ca'
       company: 'Bell Canada'
index 73cca23..8a98baf 100755 (executable)
                 <artifactId>janino</artifactId>
                 <version>3.1.7</version>
             </dependency>
+            <dependency>
+                <groupId>com.hazelcast</groupId>
+                <artifactId>hazelcast-spring</artifactId>
+                <version>4.2.5</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 </project>
index 502e982..93c265a 100644 (file)
             <groupId>org.mapstruct</groupId>
             <artifactId>mapstruct-processor</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.hazelcast</groupId>
+            <artifactId>hazelcast-spring</artifactId>
+        </dependency>
         <!-- T E S T - D E P E N D E N C I E S -->
         <dependency>
             <groupId>org.spockframework</groupId>
index 000627b..a62a009 100644 (file)
 
 package org.onap.cps.ncmp.api.impl;
 
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_ANCHOR;
 import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle;
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
 import static org.onap.cps.utils.CmHandleQueryRestParametersValidator.validateModuleNameConditionProperties;
 
 import java.util.ArrayList;
@@ -39,9 +36,8 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-import org.onap.cps.spi.CpsAdminPersistenceService;
-import org.onap.cps.spi.CpsDataPersistenceService;
 import org.onap.cps.spi.model.Anchor;
 import org.onap.cps.spi.model.CmHandleQueryServiceParameters;
 import org.onap.cps.spi.model.ConditionProperties;
@@ -56,8 +52,7 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
     private static final String PROPERTY_QUERY_NAME = "hasAllProperties";
     private static final String MODULE_QUERY_NAME = "hasAllModules";
     private static final Map<String, NcmpServiceCmHandle> NO_QUERY_EXECUTED = null;
-    private final CpsDataPersistenceService cpsDataPersistenceService;
-    private final CpsAdminPersistenceService cpsAdminPersistenceService;
+    private final InventoryPersistence inventoryPersistence;
 
     /**
      * Query and return cm handles that match the given query parameters.
@@ -128,7 +123,7 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
             final String cpsPath = "//public-properties[@name='" + entry.getKey() + "' and @value='"
                 + entry.getValue() + "']/ancestor::cm-handles";
 
-            final Collection<DataNode> dataNodes = queryDataNodes(cpsPath);
+            final Collection<DataNode> dataNodes = inventoryPersistence.queryDataNodes(cpsPath);
             if (cmHandleIdToNcmpServiceCmHandles == NO_QUERY_EXECUTED) {
                 cmHandleIdToNcmpServiceCmHandles = collectDataNodesToNcmpServiceCmHandles(dataNodes);
             } else {
@@ -159,7 +154,7 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
         if (previousQueryResult == NO_QUERY_EXECUTED) {
             cmHandleIdsByModuleName.forEach(cmHandleId ->
                     queryResult.put(cmHandleId, createNcmpServiceCmHandle(
-                            getDataNode("/dmi-registry/cm-handles[@id='" + cmHandleId + "']")))
+                            inventoryPersistence.getDataNode("/dmi-registry/cm-handles[@id='" + cmHandleId + "']")))
             );
             return queryResult;
         }
@@ -169,8 +164,7 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
     }
 
     private Set<String> getNamesOfAnchorsWithGivenModules(final Collection<String> moduleNamesForQuery) {
-        final Collection<Anchor> anchors =
-            cpsAdminPersistenceService.queryAnchors("NFP-Operational", moduleNamesForQuery);
+        final Collection<Anchor> anchors = inventoryPersistence.queryAnchors(moduleNamesForQuery);
         return anchors.parallelStream().map(Anchor::getName).collect(Collectors.toSet());
     }
 
@@ -212,23 +206,12 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
     }
 
     private Set<NcmpServiceCmHandle> getAllCmHandles() {
-        return getDataNode("/dmi-registry").getChildDataNodes().stream()
-            .map(this::createNcmpServiceCmHandle).collect(Collectors.toSet());
+        return inventoryPersistence.getDataNode("/dmi-registry")
+            .getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet());
     }
 
     private Set<String> getAllCmHandleIds() {
-        return cpsAdminPersistenceService.getAnchors("NFP-Operational")
-            .parallelStream().map(Anchor::getName).collect(Collectors.toSet());
-    }
-
-    private List<DataNode> queryDataNodes(final String cpsPath) {
-        return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                cpsPath, INCLUDE_ALL_DESCENDANTS);
-    }
-
-    private DataNode getDataNode(final String cpsPath) {
-        return cpsDataPersistenceService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                cpsPath, INCLUDE_ALL_DESCENDANTS);
+        return inventoryPersistence.getAnchors().parallelStream().map(Anchor::getName).collect(Collectors.toSet());
     }
 
     private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) {
index 044a5a4..d827d46 100755 (executable)
 
 package org.onap.cps.ncmp.api.impl;
 
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_ANCHOR;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_PARENT;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
-import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
 import static org.onap.cps.utils.CmHandleQueryRestParametersValidator.validateCmHandleQueryParameters;
 
 import java.util.ArrayList;
@@ -41,8 +35,6 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsDataService;
-import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
@@ -61,7 +53,6 @@ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 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.spi.model.CmHandleQueryServiceParameters;
 import org.onap.cps.spi.model.ModuleDefinition;
 import org.onap.cps.spi.model.ModuleReference;
@@ -75,14 +66,10 @@ import org.springframework.stereotype.Service;
 @RequiredArgsConstructor
 public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService {
 
-    private final CpsDataService cpsDataService;
-
     private final JsonObjectMapper jsonObjectMapper;
 
     private final DmiDataOperations dmiDataOperations;
 
-    private final CpsModuleService cpsModuleService;
-
     private final NetworkCmProxyDataServicePropertyHandler networkCmProxyDataServicePropertyHandler;
 
     private final InventoryPersistence inventoryPersistence;
@@ -147,11 +134,10 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
             requestData, dataType);
     }
 
-
     @Override
     public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) {
         CpsValidator.validateNameCharacters(cmHandleId);
-        return cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
+        return inventoryPersistence.getYangResourcesModuleReferences(cmHandleId);
     }
 
     @Override
@@ -279,9 +265,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         for (final String cmHandle : tobeRemovedCmHandles) {
             try {
                 CpsValidator.validateNameCharacters(cmHandle);
-                deleteSchemaSetWithCascade(cmHandle);
-                cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                        "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
+                inventoryPersistence.deleteSchemaSetWithCascade(cmHandle);
+                inventoryPersistence.deleteListOrListElement("/dmi-registry/cm-handles[@id='" + cmHandle + "']");
                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandle));
             } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
                 log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
@@ -303,21 +288,11 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         return cmHandleRegistrationResponses;
     }
 
-    private void deleteSchemaSetWithCascade(final String schemaSetName) {
-        try {
-            cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
-                    CASCADE_DELETE_ALLOWED);
-        } catch (final SchemaSetNotFoundException schemaSetNotFoundException) {
-            log.warn("Schema set {} does not exist or already deleted", schemaSetName);
-        }
-    }
-
     private CmHandleRegistrationResponse registerNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
         try {
             final String cmHandleJsonData = String.format("{\"cm-handles\":[%s]}",
                     jsonObjectMapper.asJsonString(yangModelCmHandle));
-            cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
-                    cmHandleJsonData, NO_TIMESTAMP);
+            inventoryPersistence.saveListElements(cmHandleJsonData);
             return CmHandleRegistrationResponse.createSuccessResponse(yangModelCmHandle.getId());
         } catch (final AlreadyDefinedException alreadyDefinedException) {
             return CmHandleRegistrationResponse.createFailureResponse(
index aae2f20..d9aeaf2 100644 (file)
@@ -23,10 +23,6 @@ package org.onap.cps.ncmp.api.impl;
 
 import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY;
 import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_ANCHOR;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_PARENT;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
 
 import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
@@ -39,11 +35,10 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsDataService;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
 import org.onap.cps.spi.exceptions.DataValidationException;
 import org.onap.cps.spi.model.DataNode;
@@ -58,9 +53,7 @@ import org.springframework.stereotype.Service;
 @SuppressWarnings("squid:S5852")
 public class NetworkCmProxyDataServicePropertyHandler {
 
-    private static final String CM_HANDLE_XPATH_TEMPLATE = NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='%s']";
-
-    private final CpsDataService cpsDataService;
+    private final InventoryPersistence inventoryPersistence;
 
     /**
      * Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes.
@@ -72,31 +65,28 @@ public class NetworkCmProxyDataServicePropertyHandler {
         final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
         for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
-            final String cmHandle = ncmpServiceCmHandle.getCmHandleId();
+            final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
             try {
-                CpsValidator.validateNameCharacters(cmHandle);
-                final String cmHandleXpath = String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandle);
-                final DataNode existingCmHandleDataNode =
-                        cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXpath,
-                                FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+                CpsValidator.validateNameCharacters(cmHandleId);
+                final DataNode existingCmHandleDataNode = inventoryPersistence.getCmHandleDataNode(cmHandleId);
                 processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle);
-                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandle));
+                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId));
             } catch (final DataNodeNotFoundException e) {
                 log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
-                    cmHandle, e.getMessage());
+                    cmHandleId, e.getMessage());
                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
-                    .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
+                    .createFailureResponse(cmHandleId, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
             } catch (final DataValidationException e) {
                 log.error("Unable to update cm handle : {}, caused by : {}",
-                    cmHandle, e.getMessage());
+                    cmHandleId, e.getMessage());
                 cmHandleRegistrationResponses.add(
-                    CmHandleRegistrationResponse.createFailureResponse(cmHandle,
+                    CmHandleRegistrationResponse.createFailureResponse(cmHandleId,
                         RegistrationError.CM_HANDLE_INVALID_ID));
             } catch (final Exception exception) {
                 log.error("Unable to update cmHandle : {} , caused by : {}",
-                    cmHandle, exception.getMessage());
+                    cmHandleId, exception.getMessage());
                 cmHandleRegistrationResponses.add(
-                    CmHandleRegistrationResponse.createFailureResponse(cmHandle, exception));
+                    CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception));
             }
         }
         return cmHandleRegistrationResponses;
@@ -120,8 +110,7 @@ public class NetworkCmProxyDataServicePropertyHandler {
         if (replacementPropertyDataNodes.isEmpty()) {
             removeAllProperties(existingCmHandleDataNode, propertyType);
         } else {
-            cpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                    existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes, NO_TIMESTAMP);
+            inventoryPersistence.replaceListContent(existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes);
         }
     }
 
@@ -130,8 +119,7 @@ public class NetworkCmProxyDataServicePropertyHandler {
             final Matcher matcher = propertyType.propertyXpathPattern.matcher(dataNode.getXpath());
             if (matcher.find()) {
                 log.info("Deleting dataNode with xpath : [{}]", dataNode.getXpath());
-                cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, dataNode.getXpath(),
-                        NO_TIMESTAMP);
+                inventoryPersistence.deleteDataNode(dataNode.getXpath());
             }
         });
     }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfig.java
new file mode 100644 (file)
index 0000000..978c3d1
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * ============LICENSE_START========================================================
+ *  Copyright (C) 2022 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.api.impl.config.embeddedcache;
+
+import com.hazelcast.config.Config;
+import com.hazelcast.config.MapConfig;
+import com.hazelcast.core.Hazelcast;
+import java.util.Map;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Core infrastructure of the hazelcast distributed map for Module Sync and Data Sync use cases.
+ */
+@Configuration
+public class SynchronizationSemaphoresConfig {
+
+    /**
+     * Module Sync Distributed Map Instance.
+     * @return  Instance of Map
+     */
+    @Bean
+    public Map<String, String> moduleSyncSemaphore() {
+        return Hazelcast.newHazelcastInstance(
+                initializeDefaultMapConfig("moduleSyncSemaphore", "moduleSyncSemaphoreConfig"))
+                .getMap("moduleSyncSemaphore");
+    }
+
+    /**
+     * Data Sync Distributed Map Instance.
+     * @return  Instance of Map
+     */
+    @Bean
+    public Map<String, String> dataSyncSemaphore() {
+        return Hazelcast.newHazelcastInstance(
+                initializeDefaultMapConfig("dataSyncSemaphore", "dataSyncSemaphoreConfig"))
+                .getMap("dataSyncSemaphore");
+    }
+
+    private Config initializeDefaultMapConfig(final String instanceName, final String configName) {
+        final Config config = new Config(instanceName);
+        final MapConfig mapConfig = new MapConfig(configName);
+        mapConfig.setTimeToLiveSeconds(30);
+        mapConfig.setBackupCount(3);
+        mapConfig.setAsyncBackupCount(3);
+        config.addMapConfig(mapConfig);
+        config.setClusterName("synchronization-semaphores");
+        return config;
+    }
+}
index c29c725..a133cfb 100644 (file)
@@ -32,11 +32,5 @@ public final class DmiRegistryConstants {
 
     public static final String NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME = "NFP-Operational";
 
-    public static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
-
-    public static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
-
-    public static final String NCMP_DMI_REGISTRY_PARENT = "/dmi-registry";
-
     public static final OffsetDateTime NO_TIMESTAMP = null;
 }
index d47da6c..af01fb4 100644 (file)
 package org.onap.cps.ncmp.api.inventory;
 
 import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
+import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
+import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
 
 import java.time.OffsetDateTime;
 import java.util.Collection;
 import java.util.List;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.spi.CpsAdminPersistenceService;
 import org.onap.cps.spi.CpsDataPersistenceService;
 import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
+import org.onap.cps.spi.model.Anchor;
 import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.model.ModuleDefinition;
+import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.utils.CpsValidator;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.stereotype.Component;
 
+@Slf4j
 @RequiredArgsConstructor
 @Component
 public class InventoryPersistence {
@@ -47,7 +56,9 @@ public class InventoryPersistence {
 
     private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
 
-    private String xpathCmHandle = "/dmi-registry/cm-handles[@id='" + "%s" + "']";
+    private static final String NCMP_DMI_REGISTRY_PARENT = "/dmi-registry";
+
+    private static final String CM_HANDLE_XPATH_TEMPLATE = "/dmi-registry/cm-handles[@id='" + "%s" + "']";
 
     private static final String ANCESTOR_CM_HANDLES = "\"]/ancestor::cm-handles";
 
@@ -59,6 +70,8 @@ public class InventoryPersistence {
 
     private final CpsDataPersistenceService cpsDataPersistenceService;
 
+    private final CpsAdminPersistenceService cpsAdminPersistenceService;
+
     /**
      * Get the Cm Handle Composite State from the data node.
      *
@@ -67,7 +80,7 @@ public class InventoryPersistence {
      */
     public CompositeState getCmHandleState(final String cmHandleId) {
         final DataNode stateAsDataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-            String.format(xpathCmHandle, cmHandleId) + "/state",
+            String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId) + "/state",
             FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
         return new CompositeStateBuilder().fromDataNode(stateAsDataNode).build();
     }
@@ -82,7 +95,7 @@ public class InventoryPersistence {
         final String cmHandleJsonData = String.format("{\"state\":%s}",
             jsonObjectMapper.asJsonString(compositeState));
         cpsDataService.replaceNodeTree(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-            String.format(xpathCmHandle, cmHandleId),
+            String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId),
             cmHandleJsonData, OffsetDateTime.now());
     }
 
@@ -157,11 +170,121 @@ public class InventoryPersistence {
         return cpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
     }
 
-    private DataNode getCmHandleDataNode(final String cmHandle) {
-        return cpsDataService.getDataNode(NCMP_DATASPACE_NAME,
-            NCMP_DMI_REGISTRY_ANCHOR,
-            String.format(xpathCmHandle, cmHandle),
-            FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+    /**
+     * Method to return module references by cmHandleId.
+     *
+     * @param cmHandleId cm handle ID
+     * @return a collection of module references (moduleName and revision)
+     */
+    public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) {
+        CpsValidator.validateNameCharacters(cmHandleId);
+        return cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
     }
 
+    /**
+     * Method to save list elements.
+     *
+     * @param cmHandleJsonData cmHandle JSON data
+     */
+    public void saveListElements(final String cmHandleJsonData) {
+        cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
+                    cmHandleJsonData, NO_TIMESTAMP);
+    }
+
+    /**
+     * Method to delete a list or a list element.
+     *
+     * @param listElementXpath list element xPath
+     */
+    public void deleteListOrListElement(final String listElementXpath) {
+        cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+                listElementXpath, NO_TIMESTAMP);
+    }
+
+    /**
+     * Method to delete a schema set.
+     *
+     * @param schemaSetName schema set name
+     */
+    public void deleteSchemaSetWithCascade(final String schemaSetName) {
+        try {
+            CpsValidator.validateNameCharacters(schemaSetName);
+            cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
+                    CASCADE_DELETE_ALLOWED);
+        } catch (final SchemaSetNotFoundException schemaSetNotFoundException) {
+            log.warn("Schema set {} does not exist or already deleted", schemaSetName);
+        }
+    }
+
+    /**
+     * Query data nodes via cps path.
+     *
+     * @param cpsPath cps path
+     * @return List of data nodes
+     */
+    public List<DataNode> queryDataNodes(final String cpsPath) {
+        return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+                cpsPath, INCLUDE_ALL_DESCENDANTS);
+    }
+
+    /**
+     * Get data node via xpath.
+     *
+     * @param xpath xpath
+     * @return data node
+     */
+    public DataNode getDataNode(final String xpath) {
+        return cpsDataPersistenceService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+                xpath, INCLUDE_ALL_DESCENDANTS);
+    }
+
+    /**
+     * Get data node of given cm handle.
+     *
+     * @param cmHandleId cmHandle ID
+     * @return data node
+     */
+    public DataNode getCmHandleDataNode(final String cmHandleId) {
+        return this.getDataNode(String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId));
+    }
+
+    /**
+     * Query anchors via module names.
+     *
+     * @param moduleNamesForQuery module names
+     * @return Collection of anchors
+     */
+    public Collection<Anchor> queryAnchors(final Collection<String> moduleNamesForQuery) {
+        return  cpsAdminPersistenceService.queryAnchors(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery);
+    }
+
+    /**
+     * Method to get all anchors.
+     *
+     * @return Collection of anchors
+     */
+    public Collection<Anchor> getAnchors() {
+        return cpsAdminPersistenceService.getAnchors(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME);
+    }
+
+    /**
+     * Replaces list content by removing all existing elements and inserting the given new elements as data nodes.
+     *
+     * @param parentNodeXpath   parent node xpath
+     * @param dataNodes         datanodes representing the updated data
+     */
+    public void replaceListContent(final String parentNodeXpath, final Collection<DataNode> dataNodes) {
+        cpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+                parentNodeXpath, dataNodes, NO_TIMESTAMP);
+    }
+
+    /**
+     * Deletes data node for given anchor and dataspace.
+     *
+     * @param dataNodeXpath data node xpath
+     */
+    public void deleteDataNode(final String dataNodeXpath) {
+        cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, dataNodeXpath,
+                NO_TIMESTAMP);
+    }
 }
\ No newline at end of file
index 046a116..2b80b9d 100644 (file)
@@ -22,7 +22,6 @@
 package org.onap.cps.ncmp.api.inventory.sync;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import com.google.common.collect.ImmutableMap;
 import java.security.SecureRandom;
 import java.time.Duration;
 import java.time.OffsetDateTime;
@@ -190,6 +189,6 @@ public class SyncUtils {
         final JsonNode overallJsonNode = jsonObjectMapper.convertToJsonNode(jsonObjectAsString);
         final Iterator<Map.Entry<String, JsonNode>> overallJsonTreeMap = overallJsonNode.fields();
         final Map.Entry<String, JsonNode> firstElement = overallJsonTreeMap.next();
-        return jsonObjectMapper.asJsonString(ImmutableMap.of(firstElement.getKey(), firstElement.getValue()));
+        return jsonObjectMapper.asJsonString(Map.of(firstElement.getKey(), firstElement.getValue()));
     }
 }
index c121ce0..7cf572d 100644 (file)
@@ -21,8 +21,7 @@
 package org.onap.cps.ncmp.api.impl
 
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
-import org.onap.cps.spi.CpsAdminPersistenceService
-import org.onap.cps.spi.CpsDataPersistenceService
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
 import org.onap.cps.spi.model.Anchor
 import org.onap.cps.spi.model.CmHandleQueryServiceParameters
 import org.onap.cps.spi.model.ConditionProperties
@@ -33,11 +32,9 @@ import java.util.stream.Collectors
 
 class NetworkCmProxyCmHandlerQueryServiceSpec extends Specification {
 
-    def cpsDataPersistenceService = Mock(CpsDataPersistenceService)
-    def cpsAdminPersistenceService = Mock(CpsAdminPersistenceService)
+    def inventoryPersistence = Mock(InventoryPersistence)
 
-    NetworkCmProxyCmHandlerQueryService objectUnderTest = new NetworkCmProxyCmHandlerQueryServiceImpl(
-        cpsDataPersistenceService, cpsAdminPersistenceService)
+    NetworkCmProxyCmHandlerQueryService objectUnderTest = new NetworkCmProxyCmHandlerQueryServiceImpl(inventoryPersistence)
 
     def 'Retrieve cm handles with public properties when #scenario.'() {
         given: 'a condition property'
@@ -130,25 +127,36 @@ class NetworkCmProxyCmHandlerQueryServiceSpec extends Specification {
         def pNFDemo4 = new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'PNFDemo4\']', leaves: ['id':'PNFDemo4'])
         def dmiRegistry = new DataNode(xpath: '/dmi-registry', childDataNodes: [pNFDemo1, pNFDemo2, pNFDemo3, pNFDemo4])
 
-        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\'Contact\' and @value=\'newemailforstore@bookstore.com\']/ancestor::cm-handles', _) >> [pNFDemo1, pNFDemo2, pNFDemo4]
-        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\'wont_match\' and @value=\'wont_match\']/ancestor::cm-handles', _) >> []
-        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\'Contact2\' and @value=\'newemailforstore2@bookstore.com\']/ancestor::cm-handles', _) >> [pNFDemo4]
-        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\'Contact2\' and @value=\'\']/ancestor::cm-handles', _) >> []
-
-        cpsDataPersistenceService.getDataNode(_, _, '/dmi-registry', _) >> dmiRegistry
-
-        cpsDataPersistenceService.getDataNode(_, _, '/dmi-registry/cm-handles[@id=\'PNFDemo1\']', _) >> pNFDemo1
-        cpsDataPersistenceService.getDataNode(_, _, '/dmi-registry/cm-handles[@id=\'PNFDemo2\']', _) >> pNFDemo2
-        cpsDataPersistenceService.getDataNode(_, _, '/dmi-registry/cm-handles[@id=\'PNFDemo3\']', _) >> pNFDemo3
-        cpsDataPersistenceService.getDataNode(_, _, '/dmi-registry/cm-handles[@id=\'PNFDemo4\']', _) >> pNFDemo4
-
-        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-001']) >> [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo3')]
-        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-004']) >> []
-        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-003', 'MODULE-NAME-002']) >> [new Anchor(name: 'PNFDemo4')]
-        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-002', 'MODULE-NAME-003']) >> [new Anchor(name: 'PNFDemo4')]
-        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-004', 'MODULE-NAME-002']) >> []
-        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-002', 'MODULE-NAME-004']) >> []
-        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-002']) >> [new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo4')]
-        cpsAdminPersistenceService.getAnchors(_) >> [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo3'), new Anchor(name: 'PNFDemo4')]
+        inventoryPersistence.queryDataNodes('//public-properties[@name=\'Contact\' and @value=\'newemailforstore@bookstore.com\']/ancestor::cm-handles')
+                >> [pNFDemo1, pNFDemo2, pNFDemo4]
+        inventoryPersistence.queryDataNodes('//public-properties[@name=\'wont_match\' and @value=\'wont_match\']/ancestor::cm-handles')
+                >> []
+        inventoryPersistence.queryDataNodes('//public-properties[@name=\'Contact2\' and @value=\'newemailforstore2@bookstore.com\']/ancestor::cm-handles')
+                >> [pNFDemo4]
+        inventoryPersistence.queryDataNodes('//public-properties[@name=\'Contact2\' and @value=\'\']/ancestor::cm-handles')
+                >> []
+        inventoryPersistence.queryDataNodes('//public-properties/ancestor::cm-handles')
+                >> [pNFDemo1, pNFDemo2, pNFDemo3, pNFDemo4]
+
+        inventoryPersistence.queryDataNodes('//cm-handles[@id=\'PNFDemo\']') >> [pNFDemo1]
+        inventoryPersistence.queryDataNodes('//cm-handles[@id=\'PNFDemo2\']') >> [pNFDemo2]
+        inventoryPersistence.queryDataNodes('//cm-handles[@id=\'PNFDemo3\']') >> [pNFDemo3]
+        inventoryPersistence.queryDataNodes('//cm-handles[@id=\'PNFDemo4\']') >> [pNFDemo4]
+
+        inventoryPersistence.getDataNode('/dmi-registry') >> dmiRegistry
+
+        inventoryPersistence.getDataNode('/dmi-registry/cm-handles[@id=\'PNFDemo1\']') >> pNFDemo1
+        inventoryPersistence.getDataNode('/dmi-registry/cm-handles[@id=\'PNFDemo2\']') >> pNFDemo2
+        inventoryPersistence.getDataNode('/dmi-registry/cm-handles[@id=\'PNFDemo3\']') >> pNFDemo3
+        inventoryPersistence.getDataNode('/dmi-registry/cm-handles[@id=\'PNFDemo4\']') >> pNFDemo4
+
+        inventoryPersistence.queryAnchors(['MODULE-NAME-001']) >> [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo3')]
+        inventoryPersistence.queryAnchors(['MODULE-NAME-004']) >> []
+        inventoryPersistence.queryAnchors(['MODULE-NAME-003', 'MODULE-NAME-002']) >> [new Anchor(name: 'PNFDemo4')]
+        inventoryPersistence.queryAnchors(['MODULE-NAME-002', 'MODULE-NAME-003']) >> [new Anchor(name: 'PNFDemo4')]
+        inventoryPersistence.queryAnchors(['MODULE-NAME-004', 'MODULE-NAME-002']) >> []
+        inventoryPersistence.queryAnchors(['MODULE-NAME-002', 'MODULE-NAME-004']) >> []
+        inventoryPersistence.queryAnchors(['MODULE-NAME-002']) >> [new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo4')]
+        inventoryPersistence.getAnchors() >> [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo3'), new Anchor(name: 'PNFDemo4')]
     }
 }
index 02e6419..31cf31d 100644 (file)
@@ -22,9 +22,8 @@
 package org.onap.cps.ncmp.api.impl
 
 import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
-import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
+import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
 import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.ncmp.api.inventory.InventoryPersistence
@@ -51,17 +50,12 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     @Shared
     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')
 
-    @Shared
-    def cmHandlesArray = ['cmHandle001']
-
-    def mockCpsDataService = Mock(CpsDataService)
     def mockCpsModuleService = Mock(CpsModuleService)
     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
     def mockDmiDataOperations = Mock(DmiDataOperations)
     def mockNetworkCmProxyDataServicePropertyHandler = Mock(NetworkCmProxyDataServicePropertyHandler)
     def mockInventoryPersistence = Mock(InventoryPersistence)
     def stubbedNetworkCmProxyCmHandlerQueryService = Stub(NetworkCmProxyCmHandlerQueryService)
-    def noTimestamp = null
     def objectUnderTest = getObjectUnderTest()
 
     def 'DMI Registration: Create, Update & Delete operations are processed in the right order'() {
@@ -102,8 +96,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             response.getRemovedCmHandles() == removeResponses
             response.getCreatedCmHandles() == createdResponses
             response.getUpdatedCmHandles() == updateResponses
-
-
     }
 
     def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() {
@@ -158,17 +150,12 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
                 assert it.status == Status.SUCCESS
                 assert it.cmHandle == 'cmhandle'
             }
-        and: 'save list elements is invoked with the expected parameters'
-            interaction {
-                1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
-                    '/dmi-registry', _, noTimestamp) >> {
+        and: 'save list elements is invoked once with the expected parameters'
+                1 * mockInventoryPersistence.saveListElements(_) >> {
                     args -> {
-                        assert args[3].startsWith('{"cm-handles":[{"id":"cmhandle","dmi-service-name":"my-server","state":{"cm-handle-state":"ADVISED","last-update-time":"20')
-                        assert args[3].contains(expectedDmiProperties)
-                        assert args[3].contains(expectedPublicProperties)
+                        assert args[0].startsWith('{"cm-handles":[{"id":"cmhandle","dmi-service-name":"my-server","state":{"cm-handle-state":"ADVISED","last-update-time":"20')
                     }
                 }
-            }
         where:
             scenario                          | dmiProperties            | publicProperties               || expectedDmiProperties                      | expectedPublicProperties
             'with dmi & public properties'    | ['dmi-key': 'dmi-value'] | ['public-key': 'public-value'] || '[{"name":"dmi-key","value":"dmi-value"}]' | '[{"name":"public-key","value":"public-value"}]'
@@ -185,7 +172,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
                                    new NcmpServiceCmHandle(cmHandleId: 'cmhandle2'),
                                    new NcmpServiceCmHandle(cmHandleId: 'cmhandle3')])
         and: 'cm-handle creation is successful for 1st and 3rd; failed for 2nd'
-            mockCpsDataService.saveListElements(_, _, _, _, _) >> {} >> { throw new RuntimeException("Failed") } >> {}
+            mockInventoryPersistence.saveListElements(_) >> {} >> { throw new RuntimeException("Failed") } >> {}
         when: 'registration is updated to create cm-handles'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'a response is received for all cm-handles'
@@ -213,7 +200,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
             dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: cmHandleId)]
         and: 'cm-handler registration fails: #scenario'
-            mockCpsDataService.saveListElements(_, _, _, _, _) >> { throw exception }
+            mockInventoryPersistence.saveListElements(_) >> { throw exception }
         when: 'registration is updated'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'a failure response is received'
@@ -258,7 +245,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         when: 'registration is updated to delete cmhandle'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'delete list or list element is called'
-            1 * mockCpsDataService.deleteListOrListElement(_, _, _, _)
+            1 * mockInventoryPersistence.deleteListOrListElement(_)
         and: 'successful response is received'
             assert response.getRemovedCmHandles().size() == 1
             with(response.getRemovedCmHandles().get(0)) {
@@ -276,7 +263,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3'])
         and: 'cm-handle deletion is successful for 1st and 3rd; failed for 2nd'
-            mockCpsDataService.deleteListOrListElement(_, _, _, _) >> {} >> { throw new RuntimeException("Failed") } >> {}
+            mockInventoryPersistence.deleteListOrListElement(_) >> {} >> { throw new RuntimeException("Failed") } >> {}
         when: 'registration is updated to delete cmhandles'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'a response is received for all cm-handles'
@@ -304,13 +291,13 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle'])
         and: 'schema set deletion failed with unknown error'
-            mockCpsModuleService.deleteSchemaSet(_, _, _) >> { throw new RuntimeException('Failed') }
+            mockInventoryPersistence.deleteSchemaSetWithCascade(_) >> { throw new RuntimeException('Failed') }
         when: 'registration is updated to delete cmhandle'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'no exception is thrown'
             noExceptionThrown()
         and: 'cm-handle is not deleted'
-            0 * mockCpsDataService.deleteListOrListElement(_, _, _, _)
+            0 * mockInventoryPersistence.deleteListOrListElement(_)
         and: 'a failure response is received'
             assert response.getRemovedCmHandles().size() == 1
             with(response.getRemovedCmHandles().get(0)) {
@@ -326,7 +313,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle'])
         and: 'cm-handle deletion throws exception'
-            mockCpsDataService.deleteListOrListElement(_, _, _, _) >> { throw deleteListElementException }
+            mockInventoryPersistence.deleteListOrListElement(_) >> { throw deleteListElementException }
         when: 'registration is updated to delete cmhandle'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'no exception is thrown'
@@ -347,7 +334,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     }
 
     def getObjectUnderTest() {
-        return Spy(new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations,
-            mockCpsModuleService, mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, stubbedNetworkCmProxyCmHandlerQueryService))
+        return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
+            mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, stubbedNetworkCmProxyCmHandlerQueryService))
     }
 }
index 0871a89..1c8b561 100644 (file)
@@ -47,7 +47,6 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum
 import org.onap.cps.utils.JsonObjectMapper
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.api.CpsDataService
-import org.onap.cps.api.CpsModuleService
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.model.DataNode
@@ -58,7 +57,6 @@ import spock.lang.Specification
 class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def mockCpsDataService = Mock(CpsDataService)
-    def mockCpsModuleService = Mock(CpsModuleService)
     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
     def mockDmiDataOperations = Mock(DmiDataOperations)
     def nullNetworkCmProxyDataServicePropertyHandler = null
@@ -73,8 +71,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     @Shared
     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')
 
-    def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations,
-        mockCpsModuleService, nullNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCpsCmHandlerQueryService)
+    def objectUnderTest = new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
+        nullNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCpsCmHandlerQueryService)
 
     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
 
@@ -152,7 +150,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         when: 'yang resources is called'
             objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
         then: 'CPS module services is invoked for the correct dataspace and cm handle'
-            1 * mockCpsModuleService.getYangResourcesModuleReferences('NFP-Operational','some-cm-handle')
+            1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
     }
 
     def 'Getting Yang Resources with an invalid #scenario.'() {
@@ -161,7 +159,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         then: 'a data validation exception is thrown'
             thrown(DataValidationException)
         and: 'CPS module services is not invoked'
-            0 * mockCpsModuleService.getYangResourcesModuleReferences(*_)
+            0 * mockInventoryPersistence.getYangResourcesModuleReferences(*_)
     }
 
     def 'Get a cm handle.'() {
@@ -273,11 +271,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         when: 'parse and create cm handle in dmi registration then sync module'
             objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(mockDmiPluginRegistration)
         then: 'validate params for creating anchor and list elements'
-        1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, null) >> {
-            args -> {
-                assert args[3].startsWith('{"cm-handles":[{"id":"some-cm-handle-id","state":{"cm-handle-state":"ADVISED","last-update-time":"20')
+            1 * mockInventoryPersistence.saveListElements(_) >> {
+                args -> {
+                    assert args[0].startsWith('{"cm-handles":[{"id":"some-cm-handle-id","state":{"cm-handle-state":"ADVISED","last-update-time":"20')
+                }
             }
-        }
     }
 
     def 'Execute cm handle id search'() {
index 3efc9c4..64461fa 100644 (file)
@@ -21,6 +21,7 @@
 
 package org.onap.cps.ncmp.api.impl
 
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
 import org.onap.cps.spi.exceptions.DataValidationException
 
 import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST
@@ -28,9 +29,7 @@ import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Registra
 import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR
 import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 
-import org.onap.cps.api.CpsDataService
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
-import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.DataNodeBuilder
@@ -38,14 +37,11 @@ import spock.lang.Specification
 
 class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
-    def mockCpsDataService = Mock(CpsDataService)
+    def mockInventoryPersistence = Mock(InventoryPersistence)
 
-    def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockCpsDataService)
-    def dataspaceName = 'NCMP-Admin'
-    def anchorName = 'ncmp-dmi-registry'
+    def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence)
     def static cmHandleId = 'myHandle1'
     def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']"
-    def noTimeStamp = null
 
     def static propertyDataNodes = [new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp1']").withLeaves(['name': 'additionalProp1', 'value': 'additionalValue1']).build(),
                                     new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp2']").withLeaves(['name': 'additionalProp2', 'value': 'additionalValue2']).build(),
@@ -55,16 +51,16 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
     def 'Update CM Handle Public Properties: #scenario'() {
         given: 'the CPS service return a CM handle'
-            mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
+            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode
         and: 'an update cm handle request with public properties updates'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: updatedPublicProperties)]
         when: 'update data node leaves is called with the update request'
             objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'the replace list method is called with correct params'
-            1 * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp) >> { args ->
+            1 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_) >> { args ->
                 {
-                    assert args[3].leaves.size() == expectedPropertiesAfterUpdate.size()
-                    assert args[3].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
+                    assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size()
+                    assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
                 }
             }
         where: 'following public properties updates are made'
@@ -77,16 +73,16 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
     def 'Update DMI Properties: #scenario'() {
         given: 'the CPS service return a CM handle'
-            mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
+            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode
         and: 'an update cm handle request with DMI properties updates'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: updatedDmiProperties)]
         when: 'update data node leaves is called with the update request'
             objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'replace list method should is called with correct params'
-            expectedCallsToReplaceMethod * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp) >> { args ->
+            expectedCallsToReplaceMethod * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) >> { args ->
                 {
-                    assert args[3].leaves.size() == expectedPropertiesAfterUpdate.size()
-                    assert args[3].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
+                    assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size()
+                    assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
                 }
             }
         where: 'following DMI properties updates are made'
@@ -101,17 +97,17 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
     def 'Update CM Handle Properties, remove all properties: #scenario'() {
         given: 'the CPS service return a CM handle'
             def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, childDataNodes: originalPropertyDataNodes)
-            mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
+            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode
         and: 'an update cm handle request that removes all public properties(existing and non-existing)'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])]
         when: 'update data node leaves is called with the update request'
             objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'the replace list method is not called'
-            0 * mockCpsDataService.replaceListContent(*_)
+            0 * mockInventoryPersistence.replaceListContent(*_)
         then: 'delete data node will be called for any existing property'
-            expectedCallsToDeleteDataNode * mockCpsDataService.deleteDataNode(dataspaceName, anchorName, _, noTimeStamp) >> { arg ->
+            expectedCallsToDeleteDataNode * mockInventoryPersistence.deleteDataNode(_) >> { arg ->
                 {
-                    assert arg[2].contains("@name='publicProp")
+                    assert arg[0].contains("@name='publicProp")
                 }
             }
         where: 'following public properties updates are made'
@@ -124,7 +120,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
         given: 'cm handles request'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: [:], dmiProperties: [:])]
         and: 'data node cannot be found'
-            mockCpsDataService.getDataNode(*_) >> { throw exception }
+            mockInventoryPersistence.getCmHandleDataNode(*_) >> { throw exception }
         when: 'update data node leaves is called using correct parameters'
             def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'one failed registration response'
@@ -149,7 +145,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
                                          new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
                                          new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])]
         and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle'
-            mockCpsDataService.getDataNode(*_) >> cmHandleDataNode >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') } >> cmHandleDataNode
+            mockInventoryPersistence.getCmHandleDataNode(*_) >> cmHandleDataNode >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') } >> cmHandleDataNode
         when: 'update data node leaves is called using correct parameters'
             def cmHandleResponseList = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'response has 3 values'
@@ -171,7 +167,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
                 assert it.errorText == "cm-handle does not exist"
             }
         then: 'the replace list method is called twice'
-            2 * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp)
+            2 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_)
     }
 
     def convertToProperties(expectedPropertiesAfterUpdateAsMap) {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationSemaphoresConfigSpec.groovy
new file mode 100644 (file)
index 0000000..fe7ed9e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * ============LICENSE_START========================================================
+ *  Copyright (C) 2022 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.api.impl.config.embeddedcache
+
+import com.hazelcast.core.Hazelcast
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
+import spock.lang.Specification
+
+@SpringBootTest
+@ContextConfiguration(classes = [SynchronizationSemaphoresConfig])
+class SynchronizationSemaphoresConfigSpec extends Specification {
+
+    @Autowired
+    private Map<String, String> moduleSyncSemaphore;
+
+    @Autowired
+    private Map<String, String> dataSyncSemaphore;
+
+    def 'Embedded Sync Semaphores'() {
+        expect: 'system is able to create an instance of ModuleSyncSemaphore'
+            assert null != moduleSyncSemaphore
+        and: 'system is able to create an instance of DataSyncSemaphore'
+            assert null != dataSyncSemaphore
+        and: 'we have 2 instances'
+            assert Hazelcast.allHazelcastInstances.size() == 2
+        and: 'the names match'
+            assert Hazelcast.allHazelcastInstances.name == ['moduleSyncSemaphore', 'dataSyncSemaphore']
+    }
+}
index ad65763..fa43798 100644 (file)
@@ -23,6 +23,7 @@ package org.onap.cps.ncmp.api.inventory
 
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.DataNodeBuilder
+import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder
 import spock.lang.Specification
 
 import java.time.OffsetDateTime
@@ -63,4 +64,25 @@ class CompositeStateBuilderSpec extends Specification {
             assert compositeState.cmHandleState == CmHandleState.ADVISED
     }
 
+    def 'CompositeStateBuilder build'() {
+        given: 'A CompositeStateBuilder with all private fields set'
+            def finalCompositeStateBuilder = new CompositeStateBuilder()
+                .withCmHandleState(CmHandleState.ADVISED)
+                .withLastUpdatedTime(formattedDateAndTime.toString())
+                .withLockReason(LockReasonCategory.LOCKED_MODULE_SYNC_FAILED, 'locked details')
+                .withOperationalDataStores(DataStoreSyncState.SYNCHRONIZED, formattedDateAndTime)
+        when: 'build is called'
+            def result = finalCompositeStateBuilder.build()
+        then: 'result is of the correct type'
+            assert result.class == CompositeState.class
+        and: 'built result should have correct values'
+            assert !result.getDataSyncEnabled()
+            assert result.getLastUpdateTime() == formattedDateAndTime
+            assert result.getLockReason().getLockReasonCategory() == LockReasonCategory.LOCKED_MODULE_SYNC_FAILED
+            assert result.getLockReason().getDetails() == 'locked details'
+            assert result.getCmHandleState() == CmHandleState.ADVISED
+            assert result.getDataStores().getOperationalDataStore().getDataStoreSyncState() == DataStoreSyncState.SYNCHRONIZED
+            assert result.getDataStores().getOperationalDataStore().getLastSyncTime() == formattedDateAndTime
+    }
+
 }
index 638af32..50494c0 100644 (file)
@@ -25,11 +25,14 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.spi.CascadeDeleteAllowed
 import org.onap.cps.spi.CpsDataPersistenceService
+import org.onap.cps.spi.CpsAdminPersistenceService
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.exceptions.DataValidationException
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.ModuleDefinition
+import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Shared
 import spock.lang.Specification
@@ -38,6 +41,7 @@ import java.time.OffsetDateTime
 import java.time.ZoneOffset
 import java.time.format.DateTimeFormatter
 
+import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
 
@@ -51,7 +55,10 @@ class InventoryPersistenceSpec extends Specification {
 
     def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
 
-    def objectUnderTest = new InventoryPersistence(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService, mockCpsDataPersistenceService)
+    def mockCpsAdminPersistenceService = Mock(CpsAdminPersistenceService)
+
+    def objectUnderTest = new InventoryPersistence(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService,
+            mockCpsDataPersistenceService, mockCpsAdminPersistenceService)
 
     def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
             .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
@@ -79,7 +86,7 @@ class InventoryPersistenceSpec extends Specification {
     def "Retrieve CmHandle using datanode with #scenario."() {
         given: 'the cps data service returns a data node from the DMI registry'
             def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
-            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
+            mockCpsDataPersistenceService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
         when: 'retrieving the yang modelled cm handle'
             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
         then: 'the result has the correct id and service names'
@@ -113,7 +120,7 @@ class InventoryPersistenceSpec extends Specification {
     def "Handling missing service names as null CPS-1043."() {
         given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
             def dataNode = new DataNode(childDataNodes:[], leaves: [:])
-            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
+            mockCpsDataPersistenceService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
         when: 'retrieving the yang modelled cm handle'
             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
         then: 'the service names ae returned as null'
@@ -155,7 +162,7 @@ class InventoryPersistenceSpec extends Specification {
             def cmHandleState = CmHandleState.ADVISED
         and: 'cps data service returns a list of data nodes'
             mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+                    '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
         when: 'get cm handles by state is invoked'
             def result = objectUnderTest.getCmHandlesByState(cmHandleState)
         then: 'the returned result is a list of data nodes returned by cps data service'
@@ -167,7 +174,7 @@ class InventoryPersistenceSpec extends Specification {
             def cmHandleState = CmHandleState.READY
         and: 'cps data service returns a list of data nodes'
             mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+                    '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
         when: 'get cm handles by state and id is invoked'
             def result = objectUnderTest.getCmHandlesByIdAndState(cmHandleId, cmHandleState)
         then: 'the returned result is a list of data nodes returned by cps data service'
@@ -179,7 +186,7 @@ class InventoryPersistenceSpec extends Specification {
             def cmHandleState = CmHandleState.READY
         and: 'cps data service returns a list of data nodes'
             mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+                    '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
         when: 'get cm handles by operational sync state as UNSYNCHRONIZED is invoked'
             def result = objectUnderTest.getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED)
         then: 'the returned result is a list of data nodes returned by cps data service'
@@ -210,4 +217,97 @@ class InventoryPersistenceSpec extends Specification {
             assert result == moduleDefinitions
     }
 
+    def 'Get module references'() {
+        given: 'cps module service returns a collection of module references'
+            def moduleReferences = [new ModuleReference('moduleName','revision','namespace')]
+            mockCpsModuleService.getYangResourcesModuleReferences('NFP-Operational','some-cmHandle-Id') >> moduleReferences
+        when: 'get yang resources module references by cmHandle is invoked'
+            def result = objectUnderTest.getYangResourcesModuleReferences('some-cmHandle-Id')
+        then: 'the returned result is a collection of module definitions'
+            assert result == moduleReferences
+    }
+
+    def 'Save list elements'() {
+        when: 'the method to save list elements is called'
+            objectUnderTest.saveListElements('sample Json data')
+        then: 'the data service method to save list elements is called once'
+            1 * mockCpsDataService.saveListElements('NCMP-Admin','ncmp-dmi-registry','/dmi-registry','sample Json data',null)
+    }
+
+    def 'Delete list or list elements'() {
+        when: 'the method to delete list or list elements is called'
+            objectUnderTest.deleteListOrListElement('sample xPath')
+        then: 'the data service method to save list elements is called once'
+            1 * mockCpsDataService.deleteListOrListElement('NCMP-Admin','ncmp-dmi-registry','sample xPath',null)
+    }
+
+    def 'Delete schema set with a valid schema set name'() {
+        when: 'the method to delete schema set is called with valid schema set name'
+            objectUnderTest.deleteSchemaSetWithCascade('validSchemaSetName')
+        then: 'the module service to delete schemaSet is invoked once'
+            1 * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'validSchemaSetName', CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED)
+    }
+
+    def 'Delete schema set with an invalid schema set name'() {
+        when: 'the method to delete schema set is called with an invalid schema set name'
+            objectUnderTest.deleteSchemaSetWithCascade('invalid SchemaSet name')
+        then: 'a data validation exception is thrown'
+            thrown(DataValidationException)
+        and: 'the module service to delete schemaSet is not called'
+            0 * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'sampleSchemaSetName', CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED)
+    }
+
+    def 'Query data nodes via cpsPath'() {
+        when: 'the method to query data nodes is called'
+            objectUnderTest.queryDataNodes('sample cpsPath')
+        then: 'the data persistence service method to query data nodes is invoked once'
+            1 * mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin','ncmp-dmi-registry','sample cpsPath', INCLUDE_ALL_DESCENDANTS)
+    }
+
+    def 'Get data node via xPath'() {
+        when: 'the method to get data nodes is called'
+            objectUnderTest.getDataNode('sample xPath')
+        then: 'the data persistence service method to get data node is invoked once'
+            1 * mockCpsDataPersistenceService.getDataNode('NCMP-Admin','ncmp-dmi-registry','sample xPath', INCLUDE_ALL_DESCENDANTS)
+    }
+
+    def 'Get cmHandle data node'() {
+        given: 'expected xPath to get cmHandle data node'
+            def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']';
+        when: 'the method to get data nodes is called'
+            objectUnderTest.getCmHandleDataNode('sample cmHandleId')
+        then: 'the data persistence service method to get cmHandle data node is invoked once with expected xPath'
+            1 * mockCpsDataPersistenceService.getDataNode('NCMP-Admin','ncmp-dmi-registry',expectedXPath, INCLUDE_ALL_DESCENDANTS)
+    }
+
+    def 'Query anchors'() {
+        when: 'the method to query anchors is called'
+            objectUnderTest.queryAnchors(['sample-module-name'])
+        then: 'the admin persistence service method to query anchors is invoked once with the same parameter'
+            1 * mockCpsAdminPersistenceService.queryAnchors('NFP-Operational',['sample-module-name'])
+    }
+
+    def 'Get anchors'() {
+        when: 'the method to get anchors with no parameters is called'
+            objectUnderTest.getAnchors()
+        then: 'the admin persistence service method to query anchors is invoked once with a specific dataspace name'
+            1 * mockCpsAdminPersistenceService.getAnchors('NFP-Operational')
+    }
+
+    def 'Replace list content'() {
+        when: 'replace list content method is called with xpath and data nodes collection'
+            objectUnderTest.replaceListContent('sample xpath', [new DataNode()])
+        then: 'the cps data service method to replace list content is invoked once with same parameters'
+            1 * mockCpsDataService.replaceListContent('NCMP-Admin', 'ncmp-dmi-registry',
+                    'sample xpath', [new DataNode()], NO_TIMESTAMP);
+    }
+
+    def 'Delete data node via xPath'() {
+        when: 'Delete data node method is called with xpath as parameter'
+            objectUnderTest.deleteDataNode('sample dataNode xpath')
+        then: 'the cps data service method to delete data node is invoked once with the same xPath'
+            1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'ncmp-dmi-registry',
+                    'sample dataNode xpath', NO_TIMESTAMP);
+    }
+
 }
index e037350..cdb3e6c 100644 (file)
@@ -29,7 +29,6 @@ import org.onap.cps.spi.exceptions.AnchorNotFoundException
 import org.onap.cps.spi.exceptions.DataspaceInUseException
 import org.onap.cps.spi.exceptions.DataspaceNotFoundException
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
-import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException
 import org.onap.cps.spi.model.Anchor
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.test.context.jdbc.Sql
diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy
new file mode 100644 (file)
index 0000000..4cacc43
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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.notification
+
+import spock.lang.Specification
+
+class NotificationErrorHandlerSpec extends Specification{
+
+    NotificationErrorHandler objectUnderTest = new NotificationErrorHandler()
+
+    def 'Logging exception via notification error handler'() {
+        given: 'redirect system.out to a readable stream'
+            def systemOutAsStream = new ByteArrayOutputStream()
+            System.out = new PrintStream(systemOutAsStream)
+        when: 'some exception occurs'
+            objectUnderTest.onException(new Exception('sample exception'), 'some context')
+        then: 'log output results contains the correct error details'
+            def systemOutAsString = systemOutAsStream.toString()
+            systemOutAsString.contains('Failed to process')
+            systemOutAsString.contains('Error cause: sample exception')
+            systemOutAsString.contains('Error context: [some context]')
+    }
+}
+