Fix duplicated yang resource references V2 43/140143/4
authorToineSiebelink <toine.siebelink@est.tech>
Tue, 4 Feb 2025 18:30:20 +0000 (18:30 +0000)
committerToineSiebelink <toine.siebelink@est.tech>
Thu, 6 Feb 2025 16:04:02 +0000 (16:04 +0000)
- Store Yang module references using SchemaSetEntity instead of SQL
- Added integration test to check number of references
- Update dispatcher for integration test to only return yang resources requested modules
- refactor schema set create & upgrade methods for better re-uses and readability
- consistent naming in related methods like(new)yangResourceContentPerName
(the name can be filename, module name or schema set name) depending on the context
- replaced 'var' with actual class names in affected classes

Issue-ID: CPS-2605
Change-Id: I8870c70832ac533bd17ce8af409a071f659e4acf
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
27 files changed:
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java
cps-ri/pom.xml
cps-ri/src/main/java/org/onap/cps/ri/CpsAdminPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepository.java [deleted file]
cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepositoryImpl.java [deleted file]
cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepository.java [deleted file]
cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepositoryImpl.java [deleted file]
cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java
cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceImplSpec.groovy
cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
cps-service/src/main/java/org/onap/cps/impl/CpsDataServiceImpl.java
cps-service/src/main/java/org/onap/cps/impl/CpsModuleServiceImpl.java
cps-service/src/main/java/org/onap/cps/init/AbstractModelLoader.java
cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
cps-service/src/main/java/org/onap/cps/utils/YangParser.java
cps-service/src/main/java/org/onap/cps/yang/TimedYangTextSchemaSourceSetBuilder.java
cps-service/src/test/groovy/org/onap/cps/impl/CpsDataServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/impl/CpsModuleServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/impl/E2ENetworkSliceSpec.groovy
cps-service/src/test/groovy/org/onap/cps/impl/YangTextSchemaSourceSetCacheSpec.groovy
cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsModuleServicePerfTest.groovy

index f929d67..79f5496 100644 (file)
@@ -56,7 +56,7 @@ public class ModuleSyncService {
     @AllArgsConstructor
     private static final class ModuleDelta {
         Collection<ModuleReference> allModuleReferences;
-        Map<String, String> newModuleNameToContentMap;
+        Map<String, String> newYangResourceContentPerName;
     }
 
     /**
@@ -90,7 +90,7 @@ public class ModuleSyncService {
         if (sourceModuleSetTag.isEmpty() && targetModuleSetTag.isEmpty()) {
             final ModuleDelta moduleDelta = getModuleDelta(yangModelCmHandle, targetModuleSetTag);
             cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
-                    schemaSetName, moduleDelta.newModuleNameToContentMap, moduleDelta.allModuleReferences);
+                    schemaSetName, moduleDelta.newYangResourceContentPerName, moduleDelta.allModuleReferences);
         } else {
             syncAndCreateSchemaSet(yangModelCmHandle, schemaSetName, targetModuleSetTag);
             cpsAnchorService.updateAnchorSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, schemaSetName);
@@ -111,7 +111,7 @@ public class ModuleSyncService {
                 cpsModuleService.createSchemaSetFromModules(
                     NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
                     schemaSetName,
-                    moduleDelta.newModuleNameToContentMap,
+                    moduleDelta.newYangResourceContentPerName,
                     moduleDelta.allModuleReferences
                 );
                 log.info("Successfully created Schema Set {} for CM Handle {}", schemaSetName,
@@ -133,11 +133,11 @@ public class ModuleSyncService {
             dmiModelOperations.getModuleReferences(yangModelCmHandle, targetModuleSetTag);
         final Collection<ModuleReference> newModuleReferences =
                 cpsModuleService.identifyNewModuleReferences(allModuleReferences);
-        final Map<String, String> newYangResources = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
-                targetModuleSetTag, newModuleReferences);
+        final Map<String, String> newYangResourceContentPerName =
+            dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, targetModuleSetTag, newModuleReferences);
         log.debug("Module delta calculated for CM handle ID: {}. All references: {}. New modules: {}",
-            yangModelCmHandle.getId(), allModuleReferences, newYangResources.keySet());
-        return new ModuleDelta(allModuleReferences, newYangResources);
+            yangModelCmHandle.getId(), allModuleReferences, newYangResourceContentPerName.keySet());
+        return new ModuleDelta(allModuleReferences, newYangResourceContentPerName);
     }
 
     private void setCmHandleModuleSetTag(final YangModelCmHandle yangModelCmHandle, final String newModuleSetTag) {
index 7ae85df..aa5fad9 100644 (file)
@@ -3,7 +3,7 @@
   ============LICENSE_START=======================================================\r
   Copyright (C) 2020-2021 Pantheon.tech\r
   Modifications Copyright (C) 2020-2021 Bell Canada\r
-  Modifications Copyright (C) 2020-2024 Nordix Foundation\r
+  Modifications Copyright (C) 2020-2025 Nordix Foundation\r
   ================================================================================\r
   Licensed under the Apache License, Version 2.0 (the "License");\r
   you may not use this file except in compliance with the License.\r
@@ -33,7 +33,7 @@
     <artifactId>cps-ri</artifactId>\r
 \r
     <properties>\r
-        <minimum-coverage>0.29</minimum-coverage>\r
+        <minimum-coverage>0.31</minimum-coverage>\r
         <!-- Additional coverage is provided by integration-test module -->\r
     </properties>\r
 \r
index 494d691..588a639 100755 (executable)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2020-2024 Nordix Foundation.
+ * Copyright (C) 2020-2025 Nordix Foundation.
  * Modifications Copyright (C) 2020-2022 Bell Canada.
  * Modifications Copyright (C) 2021 Pantheon.tech
  * Modifications Copyright (C) 2022 TechMahindra Ltd.
@@ -91,10 +91,10 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
 
     @Override
     public void createAnchor(final String dataspaceName, final String schemaSetName, final String anchorName) {
-        final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
-        final var schemaSetEntity =
-            schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName);
-        final var anchorEntity = AnchorEntity.builder()
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        final SchemaSetEntity schemaSetEntity = schemaSetRepository
+            .getByDataspaceAndName(dataspaceEntity, schemaSetName);
+        final AnchorEntity anchorEntity = AnchorEntity.builder()
             .name(anchorName)
             .dataspace(dataspaceEntity)
             .schemaSet(schemaSetEntity)
@@ -114,7 +114,7 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
 
     @Override
     public Collection<Anchor> getAnchors(final String dataspaceName) {
-        final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final Collection<AnchorEntity> anchorEntities = anchorRepository.findAllByDataspace(dataspaceEntity);
         return anchorEntities.stream().map(CpsAdminPersistenceServiceImpl::toAnchor).collect(Collectors.toSet());
     }
@@ -154,14 +154,14 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
     @Transactional
     @Override
     public void deleteAnchor(final String dataspaceName, final String anchorName) {
-        final var anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
         anchorRepository.delete(anchorEntity);
     }
 
     @Transactional
     @Override
     public void deleteAnchors(final String dataspaceName, final Collection<String> anchorNames) {
-        final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         anchorRepository.deleteAllByDataspaceAndNameIn(dataspaceEntity, anchorNames);
     }
 
@@ -178,7 +178,7 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
     }
 
     private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) {
-        final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
     }
 
index aaf6165..dbc6c28 100755 (executable)
@@ -36,7 +36,6 @@ import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -103,10 +102,9 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
 
     @Override
     public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) {
-        final Set<YangResourceModuleReference> yangResourceModuleReferenceList =
+        final Collection<YangResourceModuleReference> yangResourceModuleReferences =
             yangResourceRepository.findAllModuleReferencesByDataspace(dataspaceName);
-        return yangResourceModuleReferenceList.stream().map(CpsModulePersistenceServiceImpl::toModuleReference)
-            .collect(Collectors.toList());
+        return yangResourceModuleReferences.stream().map(CpsModulePersistenceServiceImpl::toModuleReference).toList();
     }
 
     @Override
@@ -150,19 +148,24 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
 
     @Override
     @Transactional
-    public void storeSchemaSet(final String dataspaceName, final String schemaSetName,
-        final Map<String, String> moduleReferenceNameToContentMap) {
-        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
-        final Set<YangResourceEntity> yangResourceEntities = synchronizeYangResources(moduleReferenceNameToContentMap);
-        final SchemaSetEntity schemaSetEntity = new SchemaSetEntity();
-        schemaSetEntity.setName(schemaSetName);
-        schemaSetEntity.setDataspace(dataspaceEntity);
-        schemaSetEntity.setYangResources(yangResourceEntities);
-        try {
-            schemaSetRepository.save(schemaSetEntity);
-        } catch (final DataIntegrityViolationException e) {
-            throw AlreadyDefinedException.forSchemaSet(schemaSetName, dataspaceName, e);
-        }
+    @Timed(value = "cps.module.persistence.schemaset.create",
+        description = "Time taken to store a schemaset (list of module references)")
+    public void createSchemaSet(final String dataspaceName, final String schemaSetName,
+                                final Map<String, String> yangResourceContentPerName) {
+        final Set<YangResourceEntity> yangResourceEntities = synchronizeYangResources(yangResourceContentPerName);
+        createAndSaveSchemaSetEntity(dataspaceName, schemaSetName, yangResourceEntities);
+    }
+
+    @Override
+    @Transactional
+    @Timed(value = "cps.module.persistence.schemaset.createFromNewAndExistingModules",
+        description = "Time taken to store a schemaset (from new and existing)")
+    public void createSchemaSetFromNewAndExistingModules(final String dataspaceName, final String schemaSetName,
+                                                         final Map<String, String> newYangResourceContentPerName,
+                                                         final Collection<ModuleReference> allModuleReferences) {
+        synchronizeYangResources(newYangResourceContentPerName);
+        final Set<YangResourceEntity> allYangResourceEntities = getYangResourceEntities(allModuleReferences);
+        createAndSaveSchemaSetEntity(dataspaceName, schemaSetName, allYangResourceEntities);
     }
 
     @Override
@@ -179,22 +182,6 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
                 .map(CpsModulePersistenceServiceImpl::toSchemaSet).collect(Collectors.toList());
     }
 
-    @Override
-    @Transactional
-    @Timed(value = "cps.module.persistence.schemaset.store",
-        description = "Time taken to store a schemaset (list of module references)")
-    public void storeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
-                                          final Map<String, String> newModuleNameToContentMap,
-                                          final Collection<ModuleReference> allModuleReferences) {
-        storeSchemaSet(dataspaceName, schemaSetName, newModuleNameToContentMap);
-        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
-        final SchemaSetEntity schemaSetEntity =
-                schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName);
-        final List<Integer> allYangResourceIds =
-            yangResourceRepository.getResourceIdsByModuleReferences(allModuleReferences);
-        yangResourceRepository.insertSchemaSetIdYangResourceId(schemaSetEntity.getId(), allYangResourceIds);
-    }
-
     @Override
     @Transactional
     public void deleteSchemaSet(final String dataspaceName, final String schemaSetName) {
@@ -213,14 +200,16 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
 
     @Override
     @Transactional
-    public void updateSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
-                                           final Map<String, String> newModuleNameToContentMap,
-                                           final Collection<ModuleReference> allModuleReferences) {
+    public void updateSchemaSetFromNewAndExistingModules(final String dataspaceName, final String schemaSetName,
+                                                         final Map<String, String> newYangResourcesPerName,
+                                                         final Collection<ModuleReference> allModuleReferences) {
         final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final SchemaSetEntity schemaSetEntity =
             schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName);
-        storeAndLinkNewModules(newModuleNameToContentMap, schemaSetEntity);
-        updateAllModuleReferences(allModuleReferences, schemaSetEntity.getId());
+        synchronizeYangResources(newYangResourcesPerName);
+        final Set<YangResourceEntity> allYangResourceEntities = getYangResourceEntities(allModuleReferences);
+        schemaSetEntity.setYangResources(allYangResourceEntities);
+        schemaSetRepository.save(schemaSetEntity);
     }
 
     @Override
@@ -237,9 +226,35 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
         return moduleReferenceRepository.identifyNewModuleReferences(moduleReferencesToCheck);
     }
 
-    private Set<YangResourceEntity> synchronizeYangResources(
-        final Map<String, String> moduleReferenceNameToContentMap) {
-        final Map<String, YangResourceEntity> checksumToEntityMap = moduleReferenceNameToContentMap.entrySet().stream()
+    private Set<YangResourceEntity> synchronizeYangResources(final Map<String, String> yangResourceContentPerName) {
+        final Map<String, YangResourceEntity> yangResourceEntitiesPerChecksum =
+            getYangResourceEntityPerChecksum(yangResourceContentPerName);
+
+        final List<YangResourceEntity> existingYangResourceEntities =
+            yangResourceRepository.findAllByChecksumIn(yangResourceEntitiesPerChecksum.keySet());
+
+        existingYangResourceEntities.forEach(exist -> yangResourceEntitiesPerChecksum.remove(exist.getChecksum()));
+        final Collection<YangResourceEntity> newYangResourceEntities = yangResourceEntitiesPerChecksum.values();
+
+        if (!newYangResourceEntities.isEmpty()) {
+            try {
+                yangResourceRepository.saveAll(newYangResourceEntities);
+            } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
+                convertExceptionIfNeeded(dataIntegrityViolationException, newYangResourceEntities);
+            }
+        }
+
+        // return ALL yang resourceEntities
+        return ImmutableSet.<YangResourceEntity>builder()
+                .addAll(existingYangResourceEntities)
+                .addAll(newYangResourceEntities)
+                .build();
+    }
+
+    private static Map<String, YangResourceEntity> getYangResourceEntityPerChecksum(
+        final Map<String, String> yangResourceContentPerName) {
+        final Map<String, YangResourceEntity> yangResourceEntityPerChecksum =
+            yangResourceContentPerName.entrySet().stream()
             .map(entry -> {
                 final String checksum = DigestUtils.sha256Hex(entry.getValue().getBytes(StandardCharsets.UTF_8));
                 final Map<String, String> moduleNameAndRevisionMap = createModuleNameAndRevisionMap(entry.getKey(),
@@ -256,29 +271,22 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
                 YangResourceEntity::getChecksum,
                 entity -> entity
             ));
+        return yangResourceEntityPerChecksum;
+    }
 
-        final List<YangResourceEntity> existingYangResourceEntities =
-            yangResourceRepository.findAllByChecksumIn(checksumToEntityMap.keySet());
-        existingYangResourceEntities.forEach(yangFile -> checksumToEntityMap.remove(yangFile.getChecksum()));
-
-        final Collection<YangResourceEntity> newYangResourceEntities = checksumToEntityMap.values();
-        if (!newYangResourceEntities.isEmpty()) {
-            try {
-                yangResourceRepository.saveAll(newYangResourceEntities);
-            } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
-                // Throw a CPS duplicated Yang resource exception if the cause of the error is a yang checksum
-                // database constraint violation. If it is not, then throw the original exception
-                final Optional<DuplicatedYangResourceException> convertedException =
-                        convertToDuplicatedYangResourceException(
-                                dataIntegrityViolationException, newYangResourceEntities);
-                throw convertedException.isPresent() ? convertedException.get() : dataIntegrityViolationException;
-            }
+    private void createAndSaveSchemaSetEntity(final String dataspaceName,
+                                              final String schemaSetName,
+                                              final Set<YangResourceEntity> yangResourceEntities) {
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        final SchemaSetEntity schemaSetEntity = new SchemaSetEntity();
+        schemaSetEntity.setName(schemaSetName);
+        schemaSetEntity.setDataspace(dataspaceEntity);
+        schemaSetEntity.setYangResources(yangResourceEntities);
+        try {
+            schemaSetRepository.save(schemaSetEntity);
+        } catch (final DataIntegrityViolationException e) {
+            throw AlreadyDefinedException.forSchemaSet(schemaSetName, dataspaceName, e);
         }
-
-        return ImmutableSet.<YangResourceEntity>builder()
-            .addAll(existingYangResourceEntities)
-            .addAll(newYangResourceEntities)
-            .build();
     }
 
     private static Map<String, String> createModuleNameAndRevisionMap(final String sourceName, final String source) {
@@ -323,6 +331,14 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
         return RevisionSourceIdentifier.create(sourceName);
     }
 
+    private void convertExceptionIfNeeded(final DataIntegrityViolationException dataIntegrityViolationException,
+                                          final Collection<YangResourceEntity> newYangResourceEntities) {
+        final Optional<DuplicatedYangResourceException> convertedException =
+            convertToDuplicatedYangResourceException(
+                dataIntegrityViolationException, newYangResourceEntities);
+        throw convertedException.isPresent() ? convertedException.get() : dataIntegrityViolationException;
+    }
+
     /**
      * Convert the specified data integrity violation exception into a CPS duplicated Yang resource exception
      * if the cause of the error is a yang checksum database constraint violation.
@@ -372,6 +388,13 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
         return "no checksum found";
     }
 
+    private Set<YangResourceEntity> getYangResourceEntities(final Collection<ModuleReference> moduleReferences) {
+        return moduleReferences.stream().map(moduleReference ->
+                yangResourceRepository
+                    .findByModuleNameAndRevision(moduleReference.getModuleName(), moduleReference.getRevision()))
+            .collect(Collectors.toSet());
+    }
+
     private static ModuleReference toModuleReference(
         final YangResourceModuleReference yangResourceModuleReference) {
         return ModuleReference.builder()
@@ -392,20 +415,4 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
                 .dataspaceName(schemaSetEntity.getDataspace().getName()).build();
     }
 
-    private void storeAndLinkNewModules(final Map<String, String> newModuleNameToContentMap,
-                                        final SchemaSetEntity schemaSetEntity) {
-        final Set<YangResourceEntity> yangResourceEntities
-            = new HashSet<>(synchronizeYangResources(newModuleNameToContentMap));
-        schemaSetEntity.setYangResources(yangResourceEntities);
-        schemaSetRepository.save(schemaSetEntity);
-    }
-
-    private void updateAllModuleReferences(final Collection<ModuleReference> allModuleReferences,
-                                           final Integer schemaSetEntityId) {
-        yangResourceRepository.deleteSchemaSetYangResourceForSchemaSetId(schemaSetEntityId);
-        final List<Integer> allYangResourceIds =
-            yangResourceRepository.getResourceIdsByModuleReferences(allModuleReferences);
-        yangResourceRepository.insertSchemaSetIdYangResourceId(schemaSetEntityId, allYangResourceIds);
-    }
-
 }
diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepository.java
deleted file mode 100644 (file)
index 410dcc2..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2025 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.ri.repository;
-
-import java.util.List;
-
-public interface SchemaSetYangResourceRepository {
-
-
-    /**
-     * Link yang resources (ids) with a schema set (id).
-     *
-     * @param schemaSetId     the schema set id
-     * @param yangResourceIds list of yang resource ids
-     */
-    void insertSchemaSetIdYangResourceId(final Integer schemaSetId, final List<Integer> yangResourceIds);
-
-}
diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepositoryImpl.java
deleted file mode 100644 (file)
index 989809a..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2023 Nordix Foundation.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ri.repository;
-
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.PersistenceContext;
-import java.sql.PreparedStatement;
-import java.util.List;
-import org.hibernate.Session;
-import org.springframework.transaction.annotation.Transactional;
-
-@Transactional
-public class SchemaSetYangResourceRepositoryImpl implements SchemaSetYangResourceRepository {
-
-    private static final int MAX_INSERT_BATCH_SIZE = 100;
-
-    @PersistenceContext
-    private EntityManager entityManager;
-
-    @Override
-    public void insertSchemaSetIdYangResourceId(final Integer schemaSetId, final List<Integer> yangResourceIds) {
-        final Session session = entityManager.unwrap(Session.class);
-        session.doWork(connection -> {
-            try (PreparedStatement preparedStatement = connection.prepareStatement(
-                "INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES ( ?, ?)")) {
-                int sqlQueryCount = 1;
-                for (final int yangResourceId : yangResourceIds) {
-                    preparedStatement.setInt(1, schemaSetId);
-                    preparedStatement.setInt(2, yangResourceId);
-                    preparedStatement.addBatch();
-                    if (sqlQueryCount % MAX_INSERT_BATCH_SIZE == 0 || sqlQueryCount == yangResourceIds.size()) {
-                        preparedStatement.executeBatch();
-                    }
-                    sqlQueryCount++;
-                }
-            }
-        });
-    }
-}
-
diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepository.java
deleted file mode 100644 (file)
index 2875511..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ri.repository;
-
-import java.util.Collection;
-import java.util.List;
-import org.onap.cps.api.model.ModuleReference;
-
-public interface YangResourceNativeRepository {
-
-    List<Integer> getResourceIdsByModuleReferences(Collection<ModuleReference> moduleReferences);
-
-}
diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepositoryImpl.java
deleted file mode 100644 (file)
index 34f1ee3..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ri.repository;
-
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.PersistenceContext;
-import jakarta.persistence.Query;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.StringJoiner;
-import lombok.extern.slf4j.Slf4j;
-import org.hibernate.type.StandardBasicTypes;
-import org.onap.cps.api.model.ModuleReference;
-import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Transactional;
-
-@Slf4j
-@Repository
-public class YangResourceNativeRepositoryImpl implements YangResourceNativeRepository {
-
-    @PersistenceContext
-    private EntityManager entityManager;
-
-    @Override
-    @Transactional
-    public List<Integer> getResourceIdsByModuleReferences(final Collection<ModuleReference> moduleReferences) {
-        if (moduleReferences.isEmpty()) {
-            return Collections.emptyList();
-        }
-        final Query query = entityManager.createNativeQuery(getCombinedSelectSqlQuery(moduleReferences))
-            .unwrap(org.hibernate.query.NativeQuery.class)
-            .addScalar("id", StandardBasicTypes.INTEGER);
-        final List<Integer> yangResourceIds = query.getResultList();
-        if (yangResourceIds.size() != moduleReferences.size()) {
-            log.warn("ModuleReferences size : {} and QueryResult size : {}", moduleReferences.size(),
-                    yangResourceIds.size());
-        }
-        return yangResourceIds;
-    }
-
-    private String getCombinedSelectSqlQuery(final Collection<ModuleReference> moduleReferences) {
-        final StringJoiner sqlQueryJoiner = new StringJoiner(" UNION ALL ");
-        moduleReferences.forEach(moduleReference ->
-            sqlQueryJoiner.add(String.format("SELECT id FROM yang_resource WHERE module_name='%s' and revision='%s'",
-                moduleReference.getModuleName(),
-                moduleReference.getRevision()))
-        );
-        return sqlQueryJoiner.toString();
-    }
-}
index 628502f..e36e376 100644 (file)
@@ -32,8 +32,10 @@ import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Repository;
 
 @Repository
-public interface YangResourceRepository extends JpaRepository<YangResourceEntity, Integer>,
-    YangResourceNativeRepository, SchemaSetYangResourceRepository {
+public interface YangResourceRepository extends JpaRepository<YangResourceEntity, Integer> {
+
+    YangResourceEntity findByModuleNameAndRevision(@Param("moduleName") String moduleName,
+                                                   @Param("revision") String revision);
 
     List<YangResourceEntity> findAllByChecksumIn(Collection<String> checksums);
 
@@ -87,10 +89,6 @@ public interface YangResourceRepository extends JpaRepository<YangResourceEntity
             @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName,
             @Param("moduleName") String moduleName, @Param("revision") String revision);
 
-    @Modifying
-    @Query(value = "DELETE FROM schema_set_yang_resources WHERE schema_set_id = :schemaSetId", nativeQuery = true)
-    void deleteSchemaSetYangResourceForSchemaSetId(@Param("schemaSetId") int schemaSetId);
-
     @Modifying
     @Query(value = """
             DELETE FROM yang_resource WHERE NOT EXISTS (SELECT 1 FROM schema_set_yang_resources
index 2915bc8..9abfdbe 100644 (file)
@@ -78,8 +78,8 @@ class CpsModulePersistenceServiceImplSpec extends Specification {
         and: 'persisting yang resource raises db constraint exception (in case of concurrent requests for example)'
             mockYangResourceRepository.saveAll(_) >> { throw dbException }
         when: 'attempt to store schema set '
-            def newYangResourcesNameToContentMap = [(yangResourceName):yangResourceContent]
-            objectUnderTest.storeSchemaSet('my-dataspace', 'my-schema-set', newYangResourcesNameToContentMap)
+            def newYangResourceContentPerName = [(yangResourceName):yangResourceContent]
+            objectUnderTest.createSchemaSet('my-dataspace', 'my-schema-set', newYangResourceContentPerName)
         then: 'an #expectedThrownException is thrown'
             def e = thrown(expectedThrownException)
             assert e.getMessage().contains(expectedThrownExceptionMessage)
@@ -96,7 +96,7 @@ class CpsModulePersistenceServiceImplSpec extends Specification {
         def schemaSetEntity = new SchemaSetEntity(id: 1)
             mockSchemaSetRepository.getByDataspaceAndName(_, _) >> schemaSetEntity
         when: 'schema set update is requested'
-            objectUnderTest.updateSchemaSetFromModules('my-dataspace', 'my-schemaset', [:], [new ModuleReference('some module name', 'some revision name')])
+            objectUnderTest.updateSchemaSetFromNewAndExistingModules('my-dataspace', 'my-schemaset', [:], [new ModuleReference('some module name', 'some revision name')])
         then: 'no exception is thrown '
             noExceptionThrown()
     }
index 345bc88..4171374 100644 (file)
@@ -314,13 +314,13 @@ public interface CpsDataService {
      * @param dataspaceName                     source dataspace name
      * @param sourceAnchorName                  source anchor name
      * @param xpath                             xpath
-     * @param yangResourcesNameToContentMap     YANG resources (files) map where key is a name and value is content
+     * @param yangResourceContentPerName     YANG resources (files) map where key is a name and value is content
      * @param targetData                        target data to be compared in JSON string format
      * @param fetchDescendantsOption            defines the scope of data to fetch: defaulted to INCLUDE_ALL_DESCENDANTS
      * @return                                  list containing {@link DeltaReport} objects
      */
     List<DeltaReport> getDeltaByDataspaceAnchorAndPayload(String dataspaceName, String sourceAnchorName, String xpath,
-                                                          Map<String, String> yangResourcesNameToContentMap,
+                                                          Map<String, String> yangResourceContentPerName,
                                                           String targetData,
                                                           FetchDescendantsOption fetchDescendantsOption);
 
index c6b8c60..2494be4 100644 (file)
@@ -40,21 +40,21 @@ public interface CpsModuleService {
      *
      * @param dataspaceName                 dataspace name
      * @param schemaSetName                 schema set name
-     * @param yangResourcesNameToContentMap yang resources (files) as a mep where key is resource name
+     * @param yangResourceContentPerName yang resources (files) as a mep where key is resource name
      *                                      and value is content
      */
     void createSchemaSet(String dataspaceName, String schemaSetName,
-                         Map<String, String> yangResourcesNameToContentMap);
+                         Map<String, String> yangResourceContentPerName);
 
     /**
      * Create or upgrade a schema set from new modules and existing modules or only existing modules.
-     * @param dataspaceName             Dataspace name
-     * @param schemaSetName             schema set name
-     * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
-     * @param allModuleReferences       All YANG resource module references
+     * @param dataspaceName              Dataspace name
+     * @param schemaSetName              schema set name
+     * @param yangResourceContentPerName YANG resources map where key is a name and value is content
+     * @param allModuleReferences        All YANG resource module references
      */
     void createSchemaSetFromModules(String dataspaceName, String schemaSetName,
-                                    Map<String, String> newModuleNameToContentMap,
+                                    Map<String, String> yangResourceContentPerName,
                                     Collection<ModuleReference> allModuleReferences);
 
     /**
index 71e6f79..9f70ac9 100644 (file)
@@ -230,7 +230,7 @@ public class CpsDataServiceImpl implements CpsDataService {
     @Override
     public List<DeltaReport> getDeltaByDataspaceAnchorAndPayload(final String dataspaceName,
                                                                 final String sourceAnchorName, final String xpath,
-                                                                final Map<String, String> yangResourcesNameToContentMap,
+                                                                final Map<String, String> yangResourceContentPerName,
                                                                 final String targetData,
                                                                 final FetchDescendantsOption fetchDescendantsOption) {
 
@@ -243,7 +243,7 @@ public class CpsDataServiceImpl implements CpsDataService {
                 new ArrayList<>(rebuildSourceDataNodes(xpath, sourceAnchor, sourceDataNodes));
 
         final Collection<DataNode> targetDataNodes =
-                new ArrayList<>(buildTargetDataNodes(sourceAnchor, xpath, yangResourcesNameToContentMap, targetData));
+                new ArrayList<>(buildTargetDataNodes(sourceAnchor, xpath, yangResourceContentPerName, targetData));
 
         return cpsDeltaService.getDeltaReports(sourceDataNodesRebuilt, targetDataNodes);
     }
@@ -380,12 +380,12 @@ public class CpsDataServiceImpl implements CpsDataService {
     }
 
     private Collection<DataNode> buildTargetDataNodes(final Anchor sourceAnchor, final String xpath,
-                                                      final Map<String, String> yangResourcesNameToContentMap,
+                                                      final Map<String, String> yangResourceContentPerName,
                                                       final String targetData) {
-        if (yangResourcesNameToContentMap.isEmpty()) {
+        if (yangResourceContentPerName.isEmpty()) {
             return buildDataNodesWithAnchorAndXpath(sourceAnchor, xpath, targetData, ContentType.JSON);
         } else {
-            return buildDataNodesWithYangResourceAndXpath(yangResourcesNameToContentMap, xpath,
+            return buildDataNodesWithYangResourceAndXpath(yangResourceContentPerName, xpath,
                     targetData, ContentType.JSON);
         }
     }
@@ -454,12 +454,12 @@ public class CpsDataServiceImpl implements CpsDataService {
     }
 
     private Collection<DataNode> buildDataNodesWithParentNodeXpath(
-                                          final Map<String, String> yangResourcesNameToContentMap, final String xpath,
+                                          final Map<String, String> yangResourceContentPerName, final String xpath,
                                           final String nodeData, final ContentType contentType) {
 
         if (isRootNodeXpath(xpath)) {
             final ContainerNode containerNode = yangParser.parseData(contentType, nodeData,
-                    yangResourcesNameToContentMap, PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH);
+                    yangResourceContentPerName, PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH);
             final Collection<DataNode> dataNodes = new DataNodeBuilder()
                     .withContainerNode(containerNode)
                     .buildCollection();
@@ -470,7 +470,7 @@ public class CpsDataServiceImpl implements CpsDataService {
         }
         final String normalizedParentNodeXpath = CpsPathUtil.getNormalizedXpath(xpath);
         final ContainerNode containerNode =
-                yangParser.parseData(contentType, nodeData, yangResourcesNameToContentMap, normalizedParentNodeXpath);
+                yangParser.parseData(contentType, nodeData, yangResourceContentPerName, normalizedParentNodeXpath);
         final Collection<DataNode> dataNodes = new DataNodeBuilder()
                 .withParentNodeXpath(normalizedParentNodeXpath)
                 .withContainerNode(containerNode)
@@ -496,18 +496,18 @@ public class CpsDataServiceImpl implements CpsDataService {
     }
 
     private Collection<DataNode> buildDataNodesWithYangResourceAndXpath(
-                                            final Map<String, String> yangResourcesNameToContentMap, final String xpath,
+                                            final Map<String, String> yangResourceContentPerName, final String xpath,
                                             final String nodeData, final ContentType contentType) {
         if (!isRootNodeXpath(xpath)) {
             final String parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(xpath);
             if (parentNodeXpath.isEmpty()) {
-                return buildDataNodesWithParentNodeXpath(yangResourcesNameToContentMap, ROOT_NODE_XPATH,
+                return buildDataNodesWithParentNodeXpath(yangResourceContentPerName, ROOT_NODE_XPATH,
                         nodeData, contentType);
             }
-            return buildDataNodesWithParentNodeXpath(yangResourcesNameToContentMap, parentNodeXpath,
+            return buildDataNodesWithParentNodeXpath(yangResourceContentPerName, parentNodeXpath,
                     nodeData, contentType);
         }
-        return buildDataNodesWithParentNodeXpath(yangResourcesNameToContentMap, xpath, nodeData, contentType);
+        return buildDataNodesWithParentNodeXpath(yangResourceContentPerName, xpath, nodeData, contentType);
     }
 
     private static boolean isRootNodeXpath(final String xpath) {
index b16abcc..e50325c 100644 (file)
@@ -57,21 +57,21 @@ public class CpsModuleServiceImpl implements CpsModuleService {
     @Timed(value = "cps.module.service.schemaset.create",
         description = "Time taken to create (and store) a schemaset")
     public void createSchemaSet(final String dataspaceName, final String schemaSetName,
-        final Map<String, String> yangResourcesNameToContentMap) {
+        final Map<String, String> yangResourceContentPerName) {
         cpsValidator.validateNameCharacters(dataspaceName);
-        cpsModulePersistenceService.storeSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap);
+        cpsModulePersistenceService.createSchemaSet(dataspaceName, schemaSetName, yangResourceContentPerName);
         final YangTextSchemaSourceSet yangTextSchemaSourceSet =
-            timedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourcesNameToContentMap);
+            timedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourceContentPerName);
         yangTextSchemaSourceSetCache.updateCache(dataspaceName, schemaSetName, yangTextSchemaSourceSet);
     }
 
     @Override
     public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
-                                           final Map<String, String> newModuleNameToContentMap,
+                                           final Map<String, String> yangResourceContentPerName,
                                            final Collection<ModuleReference> allModuleReferences) {
         cpsValidator.validateNameCharacters(dataspaceName);
-        cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName,
-            newModuleNameToContentMap, allModuleReferences);
+        cpsModulePersistenceService.createSchemaSetFromNewAndExistingModules(dataspaceName, schemaSetName,
+            yangResourceContentPerName, allModuleReferences);
     }
 
     @Override
@@ -83,7 +83,7 @@ public class CpsModuleServiceImpl implements CpsModuleService {
     @Override
     public SchemaSet getSchemaSet(final String dataspaceName, final String schemaSetName) {
         cpsValidator.validateNameCharacters(dataspaceName);
-        final var yangTextSchemaSourceSet = yangTextSchemaSourceSetCache
+        final YangTextSchemaSourceSet yangTextSchemaSourceSet = yangTextSchemaSourceSetCache
             .get(dataspaceName, schemaSetName);
         return SchemaSet.builder().name(schemaSetName).dataspaceName(dataspaceName)
             .moduleReferences(yangTextSchemaSourceSet.getModuleReferences()).build();
@@ -130,15 +130,14 @@ public class CpsModuleServiceImpl implements CpsModuleService {
 
     @Override
     public void upgradeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
-                                            final Map<String, String> newModuleNameToContentMap,
+                                            final Map<String, String> newYangResourceContentPerModule,
                                             final Collection<ModuleReference> allModuleReferences) {
         cpsValidator.validateNameCharacters(dataspaceName);
-        cpsModulePersistenceService.updateSchemaSetFromModules(dataspaceName, schemaSetName,
-                newModuleNameToContentMap, allModuleReferences);
+        cpsModulePersistenceService.updateSchemaSetFromNewAndExistingModules(dataspaceName, schemaSetName,
+            newYangResourceContentPerModule, allModuleReferences);
         yangTextSchemaSourceSetCache.removeFromCache(dataspaceName, schemaSetName);
     }
 
-
     @Override
     public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) {
         cpsValidator.validateNameCharacters(dataspaceName);
index e864633..a802390 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023-2024 Nordix Foundation
+ *  Copyright (C) 2023-2025 Nordix Foundation
  *  Modifications Copyright (C) 2024 TechMahindra Ltd.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -77,7 +77,7 @@ public abstract class AbstractModelLoader implements ModelLoader {
         } catch (final AlreadyDefinedException alreadyDefinedException) {
             log.warn("Creating new schema set failed as schema set already exists");
         } catch (final Exception exception) {
-            log.error("Creating schema set failed: {} ", exception.getMessage());
+            log.error("Creating schema set {} failed: {} ", schemaSetName, exception.getMessage());
             throw new ModelOnboardingException("Creating schema set failed", exception.getMessage());
         }
     }
index 86ad502..02e1b6c 100755 (executable)
@@ -36,34 +36,37 @@ public interface CpsModulePersistenceService {
     /**
      * Stores Schema Set.
      *
-     * @param dataspaceName                 dataspace name
-     * @param schemaSetName                 schema set name
-     * @param yangResourcesNameToContentMap YANG resources (files) map where key is a name and value is content
+     * @param dataspaceName               dataspace name
+     * @param schemaSetName               schema set name
+     * @param yangResourceContentPerName a map of YANG resources map where key is a name and value is content
      */
-    void storeSchemaSet(String dataspaceName, String schemaSetName, Map<String, String> yangResourcesNameToContentMap);
+    void createSchemaSet(String dataspaceName, String schemaSetName, Map<String, String> yangResourceContentPerName);
 
     /**
      * Stores a new schema set from new modules and existing modules.
      *
-     * @param dataspaceName             Dataspace name
-     * @param schemaSetName             Schema set name
-     * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
-     * @param allModuleReferences       All YANG resources module references
+     * @param dataspaceName                   dataspace name
+     * @param schemaSetName                   Schema set name
+     * @param newYangResourceContentPerName   a map of only the new YANG resources
+     *                                        the key is a name and value is its content
+     * @param allModuleReferences             all YANG resources module references
      */
-    void storeSchemaSetFromModules(String dataspaceName, String schemaSetName,
-        Map<String, String> newModuleNameToContentMap, Collection<ModuleReference> allModuleReferences);
+    void createSchemaSetFromNewAndExistingModules(String dataspaceName, String schemaSetName,
+                                                  Map<String, String> newYangResourceContentPerName,
+                                                  Collection<ModuleReference> allModuleReferences);
 
     /**
      * Update an existing schema set from new modules and existing modules.
      *
-     * @param dataspaceName             Dataspace name
+     * @param dataspaceName             dataspace name
      * @param schemaSetName             Schema set name
-     * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
-     * @param allModuleReferences       All YANG resources module references
+     * @param newYangResourcesPerName    a map of only the new YANG resources
+     *                                  the key is a module name and value is its content
+     * @param allModuleReferences       all YANG resources module references
      */
-    void updateSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
-                                    final Map<String, String> newModuleNameToContentMap,
-                                    final Collection<ModuleReference> allModuleReferences);
+    void updateSchemaSetFromNewAndExistingModules(String dataspaceName, String schemaSetName,
+                                                  Map<String, String> newYangResourcesPerName,
+                                                  Collection<ModuleReference> allModuleReferences);
 
     /**
      * Checks whether a schema set exists in the specified dataspace.
index 08f450e..5dfeb2f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2024 Nordix Foundation.
+ * Copyright (C) 2024-2025 Nordix Foundation.
  * Modifications Copyright (C) 2024 TechMahindra Ltd.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -71,17 +71,17 @@ public class YangParser {
     /**
      * Parses data into (normalized) ContainerNode according to schema context for the given yang resource.
      *
-     * @param nodeData                         data string
-     * @param yangResourcesNameToContentMap    yang resource to content map
-     * @return                                 the NormalizedNode object
+     * @param nodeData                    data string
+     * @param yangResourceContentPerName  yang resource content per name
+     * @return                            the NormalizedNode object
      */
     @Timed(value = "cps.utils.yangparser.nodedata.with.parent.with.yangResourceMap.parse",
             description = "Time taken to parse node data with a parent")
     public ContainerNode parseData(final ContentType contentType,
                                    final String nodeData,
-                                   final Map<String, String> yangResourcesNameToContentMap,
+                                   final Map<String, String> yangResourceContentPerName,
                                    final String parentNodeXpath) {
-        final SchemaContext schemaContext = getSchemaContext(yangResourcesNameToContentMap);
+        final SchemaContext schemaContext = getSchemaContext(yangResourceContentPerName);
         return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_AND_PARSE);
     }
 
@@ -114,8 +114,8 @@ public class YangParser {
             anchor.getSchemaSetName()).getSchemaContext();
     }
 
-    private SchemaContext getSchemaContext(final Map<String, String> yangResourcesNameToContentMap) {
-        return timedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourcesNameToContentMap)
+    private SchemaContext getSchemaContext(final Map<String, String> yangResourceContentPerName) {
+        return timedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourceContentPerName)
                 .getSchemaContext();
     }
 
index 013faff..9b2ac94 100644 (file)
@@ -30,8 +30,8 @@ public class TimedYangTextSchemaSourceSetBuilder {
     @Timed(value = "cps.yangtextschemasourceset.build",
         description = "Time taken to build a yang text schema source set")
     public YangTextSchemaSourceSet getYangTextSchemaSourceSet(
-        final Map<String, String> yangResourcesNameToContentMap) {
-        return YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap);
+        final Map<String, String> yangResourceContentPerName) {
+        return YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName);
     }
 
 }
index 34d2b75..abcda6c 100644 (file)
@@ -248,10 +248,10 @@ class CpsDataServiceImplSpec extends Specification {
 
     def 'Get delta between anchor and payload with user provided schema #scenario'() {
         given: 'user provided schema set '
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
-            setupSchemaSetMocksForDelta(yangResourcesNameToContentMap)
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('bookstore.yang')
+            setupSchemaSetMocksForDelta(yangResourceContentPerName)
         when: 'attempt to get delta between an anchor and a JSON payload'
-            objectUnderTest.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, yangResourcesNameToContentMap, jsonData, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+            objectUnderTest.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, yangResourceContentPerName, jsonData, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
         then: 'dataspacename and anchor names are validated'
             1 * mockCpsValidator.validateNameCharacters(['some-dataspace', 'some-anchor'])
         and: 'source data nodes are fetched using appropriate persistence layer method'
@@ -285,10 +285,10 @@ class CpsDataServiceImplSpec extends Specification {
 
     def 'Delta between anchor and payload error scenario #scenario'() {
         given: 'schema set for given anchor and dataspace references bookstore model'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
-            setupSchemaSetMocksForDelta(yangResourcesNameToContentMap)
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('bookstore.yang')
+            setupSchemaSetMocksForDelta(yangResourceContentPerName)
         when: 'attempt to get delta between anchor and payload'
-            objectUnderTest.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, yangResourcesNameToContentMap, jsonData, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+            objectUnderTest.getDeltaByDataspaceAnchorAndPayload(dataspaceName, anchorName, xpath, yangResourceContentPerName, jsonData, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
         then: 'expected exception is thrown'
             thrown(DataValidationException)
         where: 'following parameters were used'
@@ -654,11 +654,11 @@ class CpsDataServiceImplSpec extends Specification {
         mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
     }
 
-    def setupSchemaSetMocksForDelta(Map<String, String> yangResourcesNameToContentMap) {
+    def setupSchemaSetMocksForDelta(Map<String, String> yangResourceContentPerName) {
         def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
-        mockTimedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourcesNameToContentMap) >> mockYangTextSchemaSourceSet
+        mockTimedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourceContentPerName) >> mockYangTextSchemaSourceSet
         mockYangTextSchemaSourceSetCache.get(_, _) >> mockYangTextSchemaSourceSet
-        def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap).getSchemaContext()
+        def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName).getSchemaContext()
         mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
     }
 
index 041ce60..48db53c 100644 (file)
@@ -56,7 +56,7 @@ class CpsModuleServiceImplSpec extends Specification {
         when: 'Create schema set method is invoked'
             objectUnderTest.createSchemaSet('someDataspace', 'schemaSetName@with Special!Characters', [:])
         then: 'Parameters are validated and processing is delegated to persistence service'
-            1 * mockCpsModulePersistenceService.storeSchemaSet('someDataspace', 'schemaSetName@with Special!Characters', [:])
+            1 * mockCpsModulePersistenceService.createSchemaSet('someDataspace', 'schemaSetName@with Special!Characters', [:])
         and: 'the CpsValidator is called on the dataspaceName'
             1 * mockCpsValidator.validateNameCharacters('someDataspace')
     }
@@ -68,16 +68,16 @@ class CpsModuleServiceImplSpec extends Specification {
         when: 'create schema set from modules method is invoked'
             objectUnderTest.createSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
         then: 'processing is delegated to persistence service'
-            1 * mockCpsModulePersistenceService.storeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
+            1 * mockCpsModulePersistenceService.createSchemaSetFromNewAndExistingModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
         and: 'the CpsValidator is called on the dataspaceName'
             1 * mockCpsValidator.validateNameCharacters('someDataspaceName')
     }
 
     def 'Create schema set from invalid resources'() {
         given: 'Invalid yang resource as name-to-content map'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('invalid.yang')
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('invalid.yang')
         when: 'Create schema set method is invoked'
-            objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap)
+            objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', yangResourceContentPerName)
         then: 'Model validation exception is thrown'
             thrown(ModelValidationException)
     }
@@ -85,7 +85,7 @@ class CpsModuleServiceImplSpec extends Specification {
     def 'Create schema set with duplicate yang resource exception in persistence layer.'() {
         given: 'the persistence layer throws an duplicated yang resource exception'
             def originalException = new DuplicatedYangResourceException('name', '123', null)
-            mockCpsModulePersistenceService.storeSchemaSet(*_) >> { throw originalException }
+            mockCpsModulePersistenceService.createSchemaSet(*_) >> { throw originalException }
         when: 'attempt to create schema set'
             objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', [:])
         then: 'the same duplicated yang resource exception is thrown (up)'
@@ -98,9 +98,9 @@ class CpsModuleServiceImplSpec extends Specification {
 
     def 'Get schema set by name and dataspace.'() {
         given: 'an already present schema set'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('bookstore.yang')
         and: 'yang resource cache returns the expected schema set'
-            mockYangTextSchemaSourceSetCache.get('someDataspace', 'schemaSetName@with Special!Characters') >> YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
+            mockYangTextSchemaSourceSetCache.get('someDataspace', 'schemaSetName@with Special!Characters') >> YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName)
         when: 'get schema set method is invoked'
             def result = objectUnderTest.getSchemaSet('someDataspace', 'schemaSetName@with Special!Characters')
         then: 'the correct schema set is returned'
index 8171209..db5b4f1 100755 (executable)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2024 Nordix Foundation.
+ * Copyright (C) 2021-2025 Nordix Foundation.
  * Modifications Copyright (C) 2021-2022 Bell Canada.
  * Modifications Copyright (C) 2021 Pantheon.tech
  * Modifications Copyright (C) 2022-2024 TechMahindra Ltd.
@@ -66,32 +66,32 @@ class E2ENetworkSliceSpec extends Specification {
 
     def 'E2E model can be parsed by CPS.'() {
         given: 'Valid yang resource as name-to-content map'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap(
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap(
                     'ietf/ietf-inet-types@2013-07-15.yang',
                     'ietf/ietf-yang-types@2013-07-15.yang',
                     'e2e/basic/ran-network2020-08-06.yang'
             )
         when: 'Create schema set method is invoked'
-            cpsModuleServiceImpl.createSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap)
+            cpsModuleServiceImpl.createSchemaSet(dataspaceName, schemaSetName, yangResourceContentPerName)
         then: 'Parameters are validated and processing is delegated to persistence service'
-            1 * mockModuleStoreService.storeSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap)
+            1 * mockModuleStoreService.createSchemaSet(dataspaceName, schemaSetName, yangResourceContentPerName)
     }
 
     def 'E2E Coverage Area-Tracking Area & TA-Cell mapping model can be parsed by CPS.'() {
         given: 'Valid yang resource as name-to-content map'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap(
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap(
                     'e2e/basic/cps-cavsta-onap-internal2021-01-28.yang')
         when: 'Create schema set method is invoked'
-            cpsModuleServiceImpl.createSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap)
+            cpsModuleServiceImpl.createSchemaSet(dataspaceName, schemaSetName, yangResourceContentPerName)
         then: 'Parameters are validated and processing is delegated to persistence service'
-            1 * mockModuleStoreService.storeSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap)
+            1 * mockModuleStoreService.createSchemaSet(dataspaceName, schemaSetName, yangResourceContentPerName)
     }
 
     def 'E2E Coverage Area-Tracking Area & TA-Cell mapping data can be parsed by CPS.'() {
         given: 'Valid yang resource as name-to-content map'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap(
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap(
                     'e2e/basic/cps-cavsta-onap-internal2021-01-28.yang')
-            def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap).getSchemaContext()
+            def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName).getSchemaContext()
             def dataNodeStored
         and : 'a valid json is provided for the model'
             def jsonData = TestUtils.getResourceFileContent('e2e/basic/cps-Cavsta-Data.txt')
@@ -99,7 +99,7 @@ class E2ENetworkSliceSpec extends Specification {
             mockCpsAnchorService.getAnchor(dataspaceName, anchorName) >>
                     new Anchor().builder().name(anchorName).schemaSetName(schemaSetName).dataspaceName(dataspaceName).build()
             mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >>
-                    YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
+                    YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName)
             mockModuleStoreService.getYangSchemaResources(dataspaceName, schemaSetName) >> schemaContext
         when: 'saveData method is invoked'
             cpsDataServiceImpl.saveData(dataspaceName, anchorName, jsonData, noTimestamp)
@@ -123,15 +123,15 @@ class E2ENetworkSliceSpec extends Specification {
     def 'E2E Coverage Area-Tracking Area & TA-Cell mapping data can be parsed for RAN inventory.'() {
         def dataNodeStored
         given: 'valid yang resource as name-to-content map'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap(
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap(
                     'e2e/basic/cps-ran-inventory@2021-01-28.yang')
-            def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap).getSchemaContext()
+            def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName).getSchemaContext()
         and : 'a valid json is provided for the model'
             def jsonData = TestUtils.getResourceFileContent('e2e/basic/cps-ran-inventory-data.json')
         and : 'all the further dependencies are mocked '
             mockCpsAnchorService.getAnchor('someDataspace', 'someAnchor') >>
                     new Anchor().builder().name('someAnchor').schemaSetName('someSchemaSet').dataspaceName(dataspaceName).build()
-            mockYangTextSchemaSourceSetCache.get('someDataspace', 'someSchemaSet') >> YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
+            mockYangTextSchemaSourceSetCache.get('someDataspace', 'someSchemaSet') >> YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName)
             mockModuleStoreService.getYangSchemaResources('someDataspace', 'someSchemaSet') >> schemaContext
         when: 'saveData method is invoked'
             cpsDataServiceImpl.saveData('someDataspace', 'someAnchor', jsonData, noTimestamp)
@@ -161,7 +161,7 @@ class E2ENetworkSliceSpec extends Specification {
 
     def 'E2E RAN Schema Model.'(){
         given: 'yang resources'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap(
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap(
                     'ietf/ietf-inet-types@2013-07-15.yang',
                     'ietf/ietf-yang-types@2013-07-15.yang',
                     'e2e/basic/cps-ran-schema-model@2021-05-19.yang'
@@ -169,7 +169,7 @@ class E2ENetworkSliceSpec extends Specification {
         and : 'json data'
             def jsonData = TestUtils.getResourceFileContent('e2e/basic/cps-ran-schema-model-data-v4.json')
         expect: 'schema context is built with no exception indicating the schema set being valid '
-            def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap).getSchemaContext()
+            def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName).getSchemaContext()
         and: 'data is parsed with no exception indicating the model match'
             new YangParserHelper().parseData(ContentType.JSON, jsonData, schemaContext, '', false) != null
     }
index 6694e77..e71782c 100644 (file)
@@ -65,13 +65,13 @@ class YangTextSchemaSourceSetCacheSpec extends Specification {
         given: 'cache is empty'
             yangResourceCacheImpl.clear()
         and: 'a schema set exists'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
-            def expectedYangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('bookstore.yang')
+            def expectedYangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName)
         when: 'schema-set information is asked'
             def result = objectUnderTest.get('my-dataspace', 'my-schemaset')
         then: 'information fetched from cps module persistence'
             1 * mockModuleStoreService.getYangSchemaResources('my-dataspace', 'my-schemaset')
-                    >> yangResourcesNameToContentMap
+                    >> yangResourceContentPerName
         and: 'stored in the cache'
             def cachedValue = getCachedValue('my-dataspace', 'my-schemaset')
             assert cachedValue.getModuleReferences() == expectedYangTextSchemaSourceSet.getModuleReferences()
@@ -83,8 +83,8 @@ class YangTextSchemaSourceSetCacheSpec extends Specification {
 
     def 'Cache Hit: Respond from cache'() {
         given: 'a schema set exists'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
-            def expectedYangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('bookstore.yang')
+            def expectedYangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName)
         and: 'stored in cache'
             yangResourceCacheImpl.put(getCacheKey('my-dataspace', 'my-schemaset'), expectedYangTextSchemaSourceSet)
         when: 'schema-set information is asked'
@@ -97,8 +97,8 @@ class YangTextSchemaSourceSetCacheSpec extends Specification {
 
     def 'Cache Update: when no data exist in the cache'() {
         given: 'a schema set exists'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
-            def yangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('bookstore.yang')
+            def yangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName)
         when: 'cache is updated'
             objectUnderTest.updateCache('my-dataspace', 'my-schemaset', yangTextSchemaSourceSet)
         then: 'cached value is same as expected'
@@ -110,8 +110,8 @@ class YangTextSchemaSourceSetCacheSpec extends Specification {
 
     def 'Cache Evict:with invalid #scenario'() {
         given: 'a schema set exists in cache'
-            def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
-            def yangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
+            def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('bookstore.yang')
+            def yangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName)
             yangResourceCacheImpl.put(getCacheKey('my-dataspace', 'my-schemaset'), yangTextSchemaSourceSet)
             def cachedValue = getCachedValue('my-dataspace', 'my-schemaset')
             assert cachedValue.getModuleReferences() == yangTextSchemaSourceSet.getModuleReferences()
index a2fadb7..cb7a16c 100644 (file)
@@ -40,7 +40,7 @@ class YangParserSpec extends Specification {
     def objectUnderTest = new YangParser(mockYangParserHelper, mockYangTextSchemaSourceSetCache, mockTimedYangTextSchemaSourceSetBuilder)
 
     def anchor = new Anchor(dataspaceName: 'my dataspace', schemaSetName: 'my schema')
-    def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
+    def yangResourceContentPerName = TestUtils.getYangResourcesAsMap('bookstore.yang')
     def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
     def mockSchemaContext = Mock(SchemaContext)
     def containerNodeFromYangUtils = Mock(ContainerNode)
@@ -91,9 +91,9 @@ class YangParserSpec extends Specification {
 
     def 'Parsing data with yang resource to context map.'() {
         given: 'the schema source set for the yang resource map is returned'
-            mockTimedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourcesNameToContentMap) >> mockYangTextSchemaSourceSet
+            mockTimedYangTextSchemaSourceSetBuilder.getYangTextSchemaSourceSet(yangResourceContentPerName) >> mockYangTextSchemaSourceSet
         when: 'parsing some json data'
-            def result = objectUnderTest.parseData(ContentType.JSON, 'some json', yangResourcesNameToContentMap, noParent)
+            def result = objectUnderTest.parseData(ContentType.JSON, 'some json', yangResourceContentPerName, noParent)
         then: 'the yang parser helper always returns a container node'
             1 * mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> containerNodeFromYangUtils
         and: 'the result is the same container node as return from yang utils'
index 3f7b25d..27d85de 100644 (file)
@@ -44,6 +44,7 @@ import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncService
 import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncWatchdog
 import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
 import org.onap.cps.ri.repository.DataspaceRepository
+import org.onap.cps.ri.repository.SchemaSetRepository
 import org.onap.cps.ri.utils.SessionManager
 import org.onap.cps.utils.JsonObjectMapper
 import org.springframework.beans.factory.annotation.Autowired
@@ -101,6 +102,12 @@ abstract class CpsIntegrationSpecBase extends Specification {
     @Autowired
     SessionManager sessionManager
 
+    @Autowired
+    DataspaceRepository dataspaceRepository
+
+    @Autowired
+    SchemaSetRepository schemaSetRepository
+
     @Autowired
     ParameterizedCmHandleQueryService networkCmProxyCmHandleQueryService
 
@@ -204,18 +211,18 @@ abstract class CpsIntegrationSpecBase extends Specification {
         return nodeCount
     }
 
-    def getBookstoreYangResourcesNameToContentMap() {
+    def getBookstoreyangResourceContentPerName() {
         def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
         def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang')
         return [bookstore: bookstoreModelFileContent, bookstoreTypes: bookstoreTypesFileContent]
     }
 
     def createStandardBookStoreSchemaSet(targetDataspace) {
-        cpsModuleService.createSchemaSet(targetDataspace, BOOKSTORE_SCHEMA_SET, getBookstoreYangResourcesNameToContentMap())
+        cpsModuleService.createSchemaSet(targetDataspace, BOOKSTORE_SCHEMA_SET, getBookstoreyangResourceContentPerName())
     }
 
     def createStandardBookStoreSchemaSet(targetDataspace, targetSchemaSet) {
-        cpsModuleService.createSchemaSet(targetDataspace, targetSchemaSet, getBookstoreYangResourcesNameToContentMap())
+        cpsModuleService.createSchemaSet(targetDataspace, targetSchemaSet, getBookstoreyangResourceContentPerName())
     }
 
     def dataspaceExists(dataspaceName) {
index c790521..11c0ff2 100644 (file)
@@ -29,6 +29,7 @@ import org.springframework.http.HttpStatus
 import org.springframework.http.MediaType
 
 import java.util.regex.Matcher
+import java.util.stream.Collectors
 
 import static org.onap.cps.integration.base.CpsIntegrationSpecBase.readResourceDataFile
 
@@ -81,7 +82,7 @@ class DmiDispatcher extends Dispatcher {
             // get module resources for a CM-handle
             case ~'^/dmi/v1/ch/(.*)/moduleResources$':
                 def cmHandleId = Matcher.lastMatcher[0][1]
-                return getModuleResourcesResponse(cmHandleId)
+                return getModuleResourcesResponse(request, cmHandleId)
 
             // pass-through data operation for a CM-handle
             case ~'^/dmi/v1/ch/(.*)/data/ds/(.*)$':
@@ -124,8 +125,13 @@ class DmiDispatcher extends Dispatcher {
         return mockResponseWithBody(HttpStatus.OK, moduleReferences)
     }
 
-    def getModuleResourcesResponse(cmHandleId) {
-        def moduleResources = '[' + getModuleNamesForCmHandle(cmHandleId).collect {
+    def getModuleResourcesResponse(request, cmHandleId) {
+        def moduleResourcesRequest = jsonSlurper.parseText(request.getBody().readUtf8())
+        def requestedModuleNames = moduleResourcesRequest.get('data').get('modules').collect{it.get('name')}
+        def candidateModuleNames = getModuleNamesForCmHandle(cmHandleId)
+        def moduleNames = candidateModuleNames.stream().filter(candidate -> requestedModuleNames.contains(candidate)).toList()
+
+        def moduleResources = '[' + moduleNames.collect {
             MODULE_RESOURCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it)
         }.join(',') + ']'
         return mockResponseWithBody(HttpStatus.OK, moduleResources)
index c787b42..49201e8 100644 (file)
@@ -53,8 +53,8 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
         }
         """
 
-    def newYangResourcesNameToContentMap = [:]
-    def moduleReferences = []
+    def yangResourceContentPerName = [:]
+    def allModuleReferences = []
     def noNewModules = [:]
     def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
     def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang')
@@ -67,9 +67,9 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     def 'Create new schema set from yang resources with #scenario'() {
         given: 'a new schema set with #numberOfModules modules'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfNewModules)
+            populateYangResourceContentPerNameAndAllModuleReferences(numberOfNewModules)
         when: 'the new schema set is created'
-            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', newYangResourcesNameToContentMap)
+            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', yangResourceContentPerName)
         then: 'the number of module references has increased by #numberOfNewModules'
             def yangResourceModuleReferences = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1)
             originalNumberOfModuleReferences + numberOfNewModules == yangResourceModuleReferences.size()
@@ -93,39 +93,43 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     def 'Create new schema set from modules with #scenario'() {
         given: 'a new schema set with #numberOfNewModules modules'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfNewModules)
+            populateYangResourceContentPerNameAndAllModuleReferences(numberOfNewModules)
         and: 'add existing module references (optional)'
-            moduleReferences.addAll(existingModuleReferences)
+            allModuleReferences.addAll(existingModuleReferences)
         when: 'the new schema set is created'
             def schemaSetName = "NewSchemaWith${numberOfNewModules}Modules"
-            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences)
+            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, yangResourceContentPerName, allModuleReferences)
         and: 'associated with a new anchor'
             cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, 'newAnchor')
         then: 'the new anchor has the correct number of modules'
             def yangResourceModuleReferences = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'newAnchor')
             assert expectedNumberOfModulesForAnchor == yangResourceModuleReferences.size()
+        and: 'the schema set has the correct number of modules too'
+            def dataspaceEntity = dataspaceRepository.getByName(FUNCTIONAL_TEST_DATASPACE_1)
+            def schemaSetEntity = schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName)
+            assert expectedNumberOfModulesForAnchor == schemaSetEntity.yangResources.size()
         cleanup:
             objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ schemaSetName.toString() ])
         where: 'the following module references are provided'
             scenario                        | numberOfNewModules | existingModuleReferences                          || expectedNumberOfModulesForAnchor
             'empty schema set'              | 0                  | [ ]                                               || 0
-            'one existing module'           | 0                  | [bookStoreModuleReference ]                       || 1
+            'one existing module'           | 0                  | [ bookStoreModuleReference ]                      || 1
             'two new modules'               | 2                  | [ ]                                               || 2
-            'two new modules, one existing' | 2                  | [bookStoreModuleReference ]                       || 3
+            'two new modules, one existing' | 2                  | [ bookStoreModuleReference ]                      || 3
             'over max batch size #modules'  | 101                | [ ]                                               || 101
             'two valid, one invalid module' | 2                  | [ new ModuleReference('NOT EXIST','IRRELEVANT') ] || 2
     }
 
     def 'Duplicate schema content.'() {
         given: 'a map of yang resources'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences(1)
+            populateYangResourceContentPerNameAndAllModuleReferences(1)
         when: 'a new schema set is created'
-            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema1', newYangResourcesNameToContentMap)
+            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema1', yangResourceContentPerName)
         then: 'the dataspace has one new module (reference)'
             def numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).size()
             assert numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded == originalNumberOfModuleReferences + 1
         when: 'a second new schema set is created'
-            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema2', newYangResourcesNameToContentMap)
+            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema2', yangResourceContentPerName)
         then: 'the dataspace has no additional module (reference)'
             assert numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded  == objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).size()
         cleanup:
@@ -134,8 +138,8 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     def 'Attempt to create schema set, error scenario: #scenario.'() {
         when: 'attempt to store schema set #schemaSetName in dataspace #dataspaceName'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences(0)
-            objectUnderTest.createSchemaSet(dataspaceName, schemaSetName, newYangResourcesNameToContentMap)
+            populateYangResourceContentPerNameAndAllModuleReferences(0)
+            objectUnderTest.createSchemaSet(dataspaceName, schemaSetName, yangResourceContentPerName)
         then: 'an #expectedException is thrown'
             thrown(expectedException)
         where: 'the following data is used'
@@ -146,7 +150,7 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     def 'Attempt to create duplicate schema set from modules.'() {
         when: 'attempt to store duplicate schema set from modules'
-            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, newYangResourcesNameToContentMap, [])
+            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, yangResourceContentPerName, [])
         then: 'an Already Defined Exception is thrown'
             thrown(AlreadyDefinedException)
     }
@@ -192,12 +196,12 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     def 'Identifying new module references with #scenario'() {
         when: 'identifyNewModuleReferences is called'
-            def result = objectUnderTest.identifyNewModuleReferences(moduleReferences)
+            def result = objectUnderTest.identifyNewModuleReferences(allModuleReferences)
         then: 'the correct module references are returned'
             assert result.size() == expectedResult.size()
             assert result.containsAll(expectedResult)
         where: 'the following data is used'
-            scenario                                | moduleReferences                                                       || expectedResult
+            scenario                                | allModuleReferences                                                    || expectedResult
             'just new module references'            | [new ModuleReference('new1', 'r1'), new ModuleReference('new2', 'r1')] || [new ModuleReference('new1', 'r1'), new ModuleReference('new2', 'r1')]
             'one new module,one existing reference' | [new ModuleReference('new1', 'r1'), bookStoreModuleReference]          || [new ModuleReference('new1', 'r1')]
             'no new module references'              | [bookStoreModuleReference]                                             || []
@@ -216,8 +220,8 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     def 'Retrieve all schema sets.'() {
         given: 'an extra schema set is stored'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences(1)
-            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema1', newYangResourcesNameToContentMap)
+            populateYangResourceContentPerNameAndAllModuleReferences(1)
+            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema1', yangResourceContentPerName)
         when: 'all schema sets are retrieved'
             def result = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1)
         then: 'the result contains all expected schema sets'
@@ -233,8 +237,8 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     def 'Delete schema sets with(out) cascade.'() {
         given: 'a schema set'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences(1)
-            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', newYangResourcesNameToContentMap)
+            populateYangResourceContentPerNameAndAllModuleReferences(1)
+            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', yangResourceContentPerName)
         and: 'optionally create anchor for the schema set'
             if (associateWithAnchor) {
                 cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', 'newAnchor')
@@ -261,11 +265,11 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     def 'Delete schema sets with shared resources.'() {
         given: 'a new schema set'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences(1)
-            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet1', newYangResourcesNameToContentMap)
+            populateYangResourceContentPerNameAndAllModuleReferences(1)
+            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet1', yangResourceContentPerName)
         and: 'another schema set which shares one yang resource (module)'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences(2)
-            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet2', newYangResourcesNameToContentMap)
+            populateYangResourceContentPerNameAndAllModuleReferences(2)
+            objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet2', yangResourceContentPerName)
         when: 'all schema sets are retrieved'
             def moduleRevisions = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).revision
         then: 'both modules (revisions) are present'
@@ -299,23 +303,23 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
         U P G R A D E
      */
 
-    def 'Upgrade schema set (with existing and new modules, no matching module set tag in NCMP)'() {
+    def 'Upgrade schema set [with existing and new modules, no matching module set tag in NCMP]'() {
         given: 'an anchor and schema set with 2 modules (to be upgraded)'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences('original', 2)
-            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, [])
+            populateYangResourceContentPerNameAndAllModuleReferences('original', 2)
+            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', yangResourceContentPerName, allModuleReferences)
             cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', 'targetAnchor')
             def yangResourceModuleReferencesBeforeUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
             assert yangResourceModuleReferencesBeforeUpgrade.size() == 2
             assert yangResourceModuleReferencesBeforeUpgrade.containsAll([new ModuleReference('original_0','2000-01-01'),new ModuleReference('original_1','2001-01-01')])
         and: 'two new 2 modules (from node)'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences('new', 2)
+            populateYangResourceContentPerNameAndAllModuleReferences('new', 2)
             def newModuleReferences = [new ModuleReference('new_0','2000-01-01'),new ModuleReference('new_1','2001-01-01')]
         and: 'a list of all module references (normally retrieved from node)'
-            def allModuleReferences = []
-            allModuleReferences.add(bookStoreModuleReference)
-            allModuleReferences.addAll(newModuleReferences)
+            def allOtherModuleReferences = []
+            allOtherModuleReferences.add(bookStoreModuleReference)
+            allOtherModuleReferences.addAll(newModuleReferences)
         when: 'the schema set is upgraded'
-            objectUnderTest.upgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, allModuleReferences)
+            objectUnderTest.upgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', yangResourceContentPerName, allOtherModuleReferences)
         then: 'the new anchor has the correct new and existing modules'
             def yangResourceModuleReferencesAfterUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
             assert yangResourceModuleReferencesAfterUpgrade.size() == 3
@@ -325,18 +329,19 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
             objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['targetSchema'])
     }
 
-    def 'Upgrade existing schema set from another anchor (used in NCMP for matching module set tag)'() {
+    def 'Upgrade existing schema set from another anchor [used in NCMP for matching module set tag]'() {
         given: 'an anchor and schema set with 1 module (target)'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences('target', 1)
-            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, [])
+            populateYangResourceContentPerNameAndAllModuleReferences('target', 1)
+            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', yangResourceContentPerName, allModuleReferences)
             cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', 'targetAnchor')
             def moduleReferencesBeforeUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
             assert moduleReferencesBeforeUpgrade.size() == 1
         and: 'another anchor and schema set with 2 other modules (source for upgrade)'
-            populateNewYangResourcesNameToContentMapAndAllModuleReferences('source', 2)
-            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'sourceSchema', newYangResourcesNameToContentMap, [])
+            populateYangResourceContentPerNameAndAllModuleReferences('source', 2)
+            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'sourceSchema', yangResourceContentPerName, allModuleReferences)
             cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'sourceSchema', 'sourceAnchor')
-            assert objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'sourceAnchor').size() == 2
+            def yangResourcesModuleReferences = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'sourceAnchor')
+            assert yangResourcesModuleReferences.size() == 2
         when: 'the target schema is upgraded using the module references from the source anchor'
             def moduleReferencesFromSourceAnchor = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'sourceAnchor')
             objectUnderTest.upgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', noNewModules, moduleReferencesFromSourceAnchor)
@@ -354,17 +359,19 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase {
         H E L P E R   M E T H O D S
      */
 
-    def populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfModules) {
-        populateNewYangResourcesNameToContentMapAndAllModuleReferences('name', numberOfModules)
+    def populateYangResourceContentPerNameAndAllModuleReferences(numberOfModules) {
+        populateYangResourceContentPerNameAndAllModuleReferences('name', numberOfModules)
     }
 
-    def populateNewYangResourcesNameToContentMapAndAllModuleReferences(namePrefix, numberOfModules) {
+    def populateYangResourceContentPerNameAndAllModuleReferences(namePrefix, numberOfModules) {
+        yangResourceContentPerName.clear()
+        allModuleReferences.clear()
         numberOfModules.times {
             def uniqueName = namePrefix + '_' + it
             def uniqueRevision = String.valueOf(2000 + it) + '-01-01'
-            moduleReferences.add(new ModuleReference(uniqueName, uniqueRevision))
+            allModuleReferences.add(new ModuleReference(uniqueName, uniqueRevision))
             def uniqueContent = NEW_RESOURCE_CONTENT.replace(NEW_RESOURCE_REVISION, uniqueRevision).replace('module test_module', 'module '+uniqueName)
-            newYangResourcesNameToContentMap.put(uniqueRevision, uniqueContent)
+            yangResourceContentPerName.put(uniqueName, uniqueContent)
         }
     }
 
index a37bb6a..d855341 100644 (file)
@@ -47,15 +47,15 @@ class CpsModuleServicePerfTest extends CpsPerfTestBase {
 
     def 'Store new schema set with many modules'() {
         when: 'a new schema set with 200 modules is stored'
-            def newYangResourcesNameToContentMap = [:]
+            def newYangResourceContentPerName = [:]
             (1..200).each {
                 def year = 2000 + it
                 def resourceName = "module${it}".toString()
                 def moduleName = "stores${it}"
                 def content = NEW_RESOURCE_CONTENT.replace('2020',String.valueOf(year)).replace('stores',moduleName)
-                newYangResourcesNameToContentMap.put(resourceName, content)
+                newYangResourceContentPerName.put(resourceName, content)
             }
-            objectUnderTest.createSchemaSet(CPS_PERFORMANCE_TEST_DATASPACE, 'perfSchemaSet', newYangResourcesNameToContentMap)
+            objectUnderTest.createSchemaSet(CPS_PERFORMANCE_TEST_DATASPACE, 'perfSchemaSet', newYangResourceContentPerName)
         then: 'the schema set is persisted correctly'
             def result =  cpsModuleService.getSchemaSet(CPS_PERFORMANCE_TEST_DATASPACE, 'perfSchemaSet')
             result.moduleReferences.size() == 200