Merge "Update operation passthrough running - Service Layer"
authorToine Siebelink <toine.siebelink@est.tech>
Mon, 15 Nov 2021 17:39:04 +0000 (17:39 +0000)
committerGerrit Code Review <gerrit@onap.org>
Mon, 15 Nov 2021 17:39:04 +0000 (17:39 +0000)
13 files changed:
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
cps-ri/src/test/resources/data/anchors-schemaset-modules.sql
cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
cps-service/src/main/java/org/onap/cps/spi/exceptions/DataNodeNotFoundException.java
docs/cps-path.rst
docs/deployment.rst
docs/design.rst
docs/modeling.rst

index 3e2bdd9..449a434 100755 (executable)
@@ -219,8 +219,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         final List<ConditionProperties> conditionProperties =
             conditions.getConditions().stream().collect(Collectors.toList());
         final CmHandles cmHandles = new CmHandles();
-        final Collection<String> cmHandleIdentifiers = processConditions(conditionProperties);
-        cmHandleIdentifiers.forEach(cmHandle -> cmHandles.setCmHandles(toCmHandleProperties(cmHandle)));
+        cmHandles.setCmHandles(toCmHandleProperties(processConditions(conditionProperties)));
         return ResponseEntity.ok(cmHandles);
     }
 
@@ -256,11 +255,13 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         return moduleNames;
     }
 
-    private CmHandleProperties toCmHandleProperties(final String cmHandleId) {
+    private CmHandleProperties toCmHandleProperties(final Collection<String> cmHandleIdentifiers) {
         final CmHandleProperties cmHandleProperties = new CmHandleProperties();
-        final CmHandleProperty cmHandleProperty = new CmHandleProperty();
-        cmHandleProperty.setCmHandleId(cmHandleId);
-        cmHandleProperties.add(cmHandleProperty);
+        for (final String cmHandleIdentifier : cmHandleIdentifiers) {
+            final CmHandleProperty cmHandleProperty = new CmHandleProperty();
+            cmHandleProperty.setCmHandleId(cmHandleIdentifier);
+            cmHandleProperties.add(cmHandleProperty);
+        }
         return cmHandleProperties;
     }
 
index cb3dc6f..e96b27d 100644 (file)
@@ -258,8 +258,8 @@ class NetworkCmProxyControllerSpec extends Specification {
         given: 'an endpoint and json data'
             def searchesEndpoint = "$ncmpBasePathV1/ch/searches"
             String jsonData = TestUtils.getResourceFileContent('cmhandle-search.json')
-        and: 'the service method is invoked with module names and returns a cm handle id'
-            mockNetworkCmProxyDataService.executeCmHandleHasAllModulesSearch(['module1', 'module2']) >> ['some-cmhandle-id']
+        and: 'the service method is invoked with module names and returns two cm handle ids'
+            mockNetworkCmProxyDataService.executeCmHandleHasAllModulesSearch(['module1', 'module2']) >> ['some-cmhandle-id1', 'some-cmhandle-id2']
         when: 'the searches api is invoked'
             def response = mvc.perform(post(searchesEndpoint)
                     .contentType(MediaType.APPLICATION_JSON)
@@ -267,7 +267,7 @@ class NetworkCmProxyControllerSpec extends Specification {
         then: 'response status returns OK'
             response.status == HttpStatus.OK.value()
         and: 'the expected response content is returned'
-            response.contentAsString == '{"cmHandles":[{"cmHandleId":"some-cmhandle-id"}]}'
+            response.contentAsString == '{"cmHandles":[{"cmHandleId":"some-cmhandle-id1"},{"cmHandleId":"some-cmhandle-id2"}]}'
     }
 
     def 'Call execute cm handle searches with unrecognized condition name.'() {
@@ -279,7 +279,7 @@ class NetworkCmProxyControllerSpec extends Specification {
                     .contentType(MediaType.APPLICATION_JSON)
                     .content(jsonData)).andReturn().response
         then: 'an empty cm handle identifier is returned'
-            response.contentAsString == '{"cmHandles":null}'
+            response.contentAsString == '{"cmHandles":[]}'
     }
 
     def 'Update resource data from passthrough running.' () {
index cd3c30b..b1bd03c 100755 (executable)
@@ -30,6 +30,7 @@ import javax.transaction.Transactional;
 import org.onap.cps.spi.CpsAdminPersistenceService;
 import org.onap.cps.spi.entities.AnchorEntity;
 import org.onap.cps.spi.entities.DataspaceEntity;
+import org.onap.cps.spi.entities.YangResourceModuleReference;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException;
 import org.onap.cps.spi.model.Anchor;
@@ -96,8 +97,9 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
     @Override
     public Collection<Anchor> queryAnchors(final String dataspaceName, final Collection<String> inputModuleNames) {
         validateDataspaceAndModuleNames(dataspaceName, inputModuleNames);
-        final Collection<AnchorEntity> anchorEntities =
-            anchorRepository.getAnchorsByDataspaceNameAndModuleNames(dataspaceName, inputModuleNames);
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        final Collection<AnchorEntity> anchorEntities = anchorRepository
+            .getAnchorsByDataspaceIdAndModuleNames(dataspaceEntity.getId(), inputModuleNames, inputModuleNames.size());
         return anchorEntities.stream().map(CpsAdminPersistenceServiceImpl::toAnchor).collect(Collectors.toSet());
     }
 
@@ -131,7 +133,7 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
         final Collection<String> inputModuleNames) {
         final Collection<String> retrievedModuleNames =
             yangResourceRepository.findAllModuleReferences(dataspaceName, inputModuleNames)
-                .stream().map(module -> module.getModuleName())
+                .stream().map(YangResourceModuleReference::getModuleName)
                 .collect(Collectors.toList());
         if (retrievedModuleNames.isEmpty()) {
             dataspaceRepository.getByName(dataspaceName);
index 8dc6c2f..c616c8f 100644 (file)
@@ -91,7 +91,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
     private static final Gson GSON = new GsonBuilder().create();
     private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})";
-    private static final String REG_EX_FOR_LIST_ELEMENT_KEY_PREDICATE = "\\[(\\@([^/]*?)){0,99}( and)*\\]$";
+    private static final Pattern REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE =
+            Pattern.compile("\\[(\\@([^\\/]{0,9999}))\\]$");
 
     @Override
     public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentXpath,
@@ -361,8 +362,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         final String parentNodeXpath = targetXpath.substring(0, targetXpath.lastIndexOf('/'));
         final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
         final String lastXpathElement = targetXpath.substring(targetXpath.lastIndexOf('/'));
-        final boolean isListElement = Pattern.compile(REG_EX_FOR_LIST_ELEMENT_KEY_PREDICATE)
-            .matcher(lastXpathElement).find();
+        final boolean isListElement = REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE.matcher(lastXpathElement).find();
         boolean targetExist;
         if (isListElement) {
             targetExist = deleteDataNode(parentFragmentEntity, targetXpath);
index 6d4cb3c..5870fd9 100755 (executable)
@@ -45,16 +45,13 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Integer> {
 
     Collection<AnchorEntity> findAllBySchemaSet(@NotNull SchemaSetEntity schemaSetEntity);
 
-    @Query(value = "SELECT DISTINCT\n"
-        + "anchor.*\n"
-        + "FROM\n"
-        + "yang_resource\n"
-        + "JOIN schema_set_yang_resources ON "
-        + "schema_set_yang_resources.yang_resource_id = yang_resource.id\n"
+    @Query(value = "SELECT anchor.* FROM yang_resource\n"
+        + "JOIN schema_set_yang_resources ON schema_set_yang_resources.yang_resource_id = yang_resource.id\n"
         + "JOIN schema_set ON schema_set.id = schema_set_yang_resources.schema_set_id\n"
         + "JOIN anchor ON anchor.schema_set_id = schema_set.id\n"
-        + "JOIN dataspace ON dataspace.id = anchor.dataspace_id AND dataspace.name = :dataspaceName\n"
-        + "WHERE yang_resource.module_Name IN (:moduleNames)", nativeQuery = true)
-    Collection<AnchorEntity> getAnchorsByDataspaceNameAndModuleNames(@Param("dataspaceName") String dataspaceName,
-        @Param("moduleNames") Collection<String> moduleNames);
+        + "WHERE schema_set.dataspace_id = :dataspaceId AND module_name IN (:moduleNames)\n"
+        + "GROUP BY anchor.id, anchor.name, anchor.dataspace_id, anchor.schema_set_id\n"
+        + "HAVING COUNT(DISTINCT module_name) = :sizeOfModuleNames", nativeQuery = true)
+    Collection<AnchorEntity> getAnchorsByDataspaceIdAndModuleNames(@Param("dataspaceId") int dataspaceId,
+        @Param("moduleNames") Collection<String> moduleNames, @Param("sizeOfModuleNames") int sizeOfModuleNames);
 }
\ No newline at end of file
index af199be..a0df2b1 100644 (file)
@@ -146,16 +146,15 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
     @Sql([CLEAR_DATA, SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES])
     def 'Query anchors that have #scenario.'() {
         when: 'all anchor are retrieved for the given dataspace name and module names'
-            def anchors = objectUnderTest.queryAnchors('DATASPACE-001', inputModuleNames)
+            def anchors = objectUnderTest.queryAnchors('dataspace-1', inputModuleNames)
         then: 'the expected anchors are returned'
             anchors.size() == expectedAnchors.size()
             anchors.containsAll(expectedAnchors)
         where: 'the following data is used'
-            scenario                                | inputModuleNames                       || expectedAnchors
-            'one module'                            | ['MODULE-NAME-001']                    || [buildAnchor('ANCHOR1', 'DATASPACE-001', 'SCHEMA-SET-001')]
-            'two modules'                           | ['MODULE-NAME-001', 'MODULE-NAME-002'] || [buildAnchor('ANCHOR1', 'DATASPACE-001', 'SCHEMA-SET-001'), buildAnchor('ANCHOR2', 'DATASPACE-001', 'SCHEMA-SET-002'), buildAnchor('ANCHOR3', 'DATASPACE-001', 'SCHEMA-SET-004')]
-            'a module attached to multiple anchors' | ['MODULE-NAME-003']                    || [buildAnchor('ANCHOR1', 'DATASPACE-001', 'SCHEMA-SET-001'), buildAnchor('ANCHOR2', 'DATASPACE-001', 'SCHEMA-SET-002')]
-            'same module with different revisions'  | ['MODULE-NAME-002']                    || [buildAnchor('ANCHOR2', 'DATASPACE-001', 'SCHEMA-SET-002'), buildAnchor('ANCHOR3', 'DATASPACE-001', 'SCHEMA-SET-004')]
+            scenario                           | inputModuleNames                                    || expectedAnchors
+            'one module'                       | ['module-name-1']                                   || [buildAnchor('anchor-2', 'dataspace-1', 'schema-set-2'), buildAnchor('anchor-1', 'dataspace-1', 'schema-set-1')]
+            'two modules'                      | ['module-name-1', 'module-name-2']                  || [buildAnchor('anchor-2', 'dataspace-1', 'schema-set-2'), buildAnchor('anchor-1', 'dataspace-1', 'schema-set-1')]
+            'no anchors for all three modules' | ['module-name-1', 'module-name-2', 'module-name-3'] || []
     }
 
     @Sql([CLEAR_DATA, SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES])
@@ -166,10 +165,9 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
             def thrownException = thrown(expectedException)
             thrownException.details.contains(expectedMessageDetails)
         where: 'the following data is used'
-            scenario                                                   | dataspaceName       | moduleNames                                  || expectedException            | expectedMessageDetails
-            'existing module in an unknown dataspace'                  | 'db-does-not-exist' | ['does-not-matter']                          || DataspaceNotFoundException   | 'db-does-not-exist'
-            'unknown module in an existing dataspace'                  | 'DATASPACE-001'     | ['module-does-not-exist']                    || ModuleNamesNotFoundException | 'module-does-not-exist'
-            'unknown module and known module in an existing dataspace' | 'DATASPACE-001'     | ['MODULE-NAME-001', 'module-does-not-exist'] || ModuleNamesNotFoundException | 'module-does-not-exist'
+            scenario                          | dataspaceName       | moduleNames                                || expectedException            | expectedMessageDetails  | messageDoesNotContain
+            'unknown dataspace'               | 'db-does-not-exist' | ['does-not-matter']                        || DataspaceNotFoundException   | 'db-does-not-exist'     | 'does-not-matter'
+            'unknown module and known module' | 'dataspace-1'       | ['module-name-1', 'module-does-not-exist'] || ModuleNamesNotFoundException | 'module-does-not-exist' | 'module-name-1'
     }
 
     def buildAnchor(def anchorName, def dataspaceName, def SchemaSetName) {
index d2b67f5..45119de 100644 (file)
 */
 
 INSERT INTO DATASPACE (ID, NAME) VALUES
-    (1001, 'DATASPACE-001'), (1002, 'DATASPACE-002');
+    (1001, 'dataspace-1'), (1002, 'dataspace-2');
 
 INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
-    (2001, 'SCHEMA-SET-001', 1001),
-    (2002, 'SCHEMA-SET-002', 1002),
-    (2003, 'SCHEMA-SET-003', 1001),
-    (2004, 'SCHEMA-SET-004', 1001);
+    (2001, 'schema-set-1', 1001),
+    (2002, 'schema-set-2', 1001),
+    (2003, 'schema-set-3', 1001),
+    (2004, 'schema-set-4', 1002);
 
 INSERT INTO YANG_RESOURCE (ID, NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES
-    (3001, 'module1@2020-02-02.yang', 'CONTENT-001', 'checksum1','MODULE-NAME-001',null),
-    (3002, 'module2@2020-02-02.yang', 'CONTENT-002', 'checksum2','MODULE-NAME-002','REVISION-002'),
-    (3003, 'module3@2020-02-02.yang', 'CONTENT-003', 'checksum3','MODULE-NAME-003','REVISION-003'),
-    (3004, 'module4@2020-02-02.yang', 'CONTENT-004', 'checksum4','MODULE-NAME-004','REVISION-004'),
-    (3005, 'module5@2020-03-02.yang', 'CONTENT-005', 'checksum5','MODULE-NAME-002','REVISION-003');
+    (3001, 'module1@revA.yang', 'some-content', 'checksum1','module-name-1','revA'),
+    (3002, 'module2@revA.yang', 'some-content', 'checksum2','module-name-2','revA'),
+    (3003, 'module2@revB.yang', 'some-content', 'checksum3','module-name-2','revB'),
+    (3004, 'module3@revA.yang', 'some-content', 'checksum4','module-name-3','revA');
 
 INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
-    (2001, 3001), (2002, 3002),
-    (2001, 3003), (2002, 3003),
-    (2003, 3004), (2004, 3005);
+    (2001, 3001), --schema-set-1(anchor-1) has modules module1@revA, module2@revA
+    (2001, 3002),
+    (2002, 3001), --schema-set-2(anchor-2) has modules module1@revA, module2@revB
+    (2002, 3003),
+    (2003, 3002), --schema-set-3(anchor-3) has modules module2@revA, module2@revB
+    (2003, 3003),
+    (2004, 3001); --schema-set-4(anchor-4) has module module1@revA but in other dataspace
 
 INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
-    (6001, 'ANCHOR1', 1001, 2001),
-    (6002, 'ANCHOR2', 1001, 2002),
-    (6003, 'ANCHOR3', 1001, 2004);
+    (6001, 'anchor-1', 1001, 2001),
+    (6002, 'anchor-2', 1001, 2002),
+    (6003, 'anchor-3', 1001, 2003),
+    (6005, 'anchor-4', 1002, 2004);
index 4640a0f..faff7b6 100755 (executable)
@@ -64,6 +64,6 @@ public class CpsAdminServiceImpl implements CpsAdminService {
     @Override
     public Collection<String> queryAnchorNames(final String dataspaceName, final Collection<String> moduleNames) {
         final Collection<Anchor> anchors = cpsAdminPersistenceService.queryAnchors(dataspaceName, moduleNames);
-        return anchors.stream().map(anchor -> anchor.getName()).collect(Collectors.toList());
+        return anchors.stream().map(Anchor::getName).collect(Collectors.toList());
     }
 }
index b717a2b..db10c88 100755 (executable)
@@ -27,7 +27,7 @@ package org.onap.cps.spi.exceptions;
 public class DataNodeNotFoundException extends DataValidationException {
 
     private static final long serialVersionUID = 7786740001662205407L;
-
+    private static final String DATANODE_NOT_FOUND = "DataNode not found";
     /**
      * Constructor.
      *
@@ -36,9 +36,10 @@ public class DataNodeNotFoundException extends DataValidationException {
      * @param xpath                 datanode xpath
      * @param additionalInformation additional information
      */
+
     public DataNodeNotFoundException(final String dataspaceName, final String anchorName, final String xpath,
                                      final String additionalInformation) {
-        super("DataNode not found", String
+        super(DATANODE_NOT_FOUND, String
             .format("DataNode with xpath %s was not found for anchor %s and dataspace %s, %s.", xpath,
                 anchorName, dataspaceName, additionalInformation));
     }
@@ -51,7 +52,7 @@ public class DataNodeNotFoundException extends DataValidationException {
      * @param xpath         datanode xpath
      */
     public DataNodeNotFoundException(final String dataspaceName, final String anchorName, final String xpath) {
-        super("DataNode not found", String
+        super(DATANODE_NOT_FOUND, String
             .format("DataNode with xpath %s was not found for anchor %s and dataspace %s.", xpath,
                 anchorName, dataspaceName));
     }
@@ -63,7 +64,7 @@ public class DataNodeNotFoundException extends DataValidationException {
      * @param anchorName the anchor name
      */
     public DataNodeNotFoundException(final String dataspaceName, final String anchorName) {
-        super("DataNode not found", String.format(
+        super(DATANODE_NOT_FOUND, String.format(
             "DataNode not found for anchor %s and dataspace %s.", anchorName, dataspaceName));
     }
 }
index 5834d68..bc46681 100644 (file)
@@ -113,7 +113,7 @@ leaf-conditions
   - ``/shops/bookstore/categories[@numberOfBooks=1]``
   - ``//categories[@name="Kids"]``
   - ``//categories[@name='Kids']``
-  - ``//categories[@code=1]/book[@title='Dune' and price=5]``
+  - ``//categories[@code=1]/books/book[@title='Dune' and @price=5]``
 
 **Limitations**
   - Only the last list or container can be queried leaf values. Any ancestor list will have to be referenced by its key name-value pair(s).
index b1839cb..2f68a64 100644 (file)
@@ -177,7 +177,7 @@ exhaustive.
 |                                       |                                                                                                         |                               |
 |                                       | If not defined, the password is generated when deploying the application.                               |                               |
 |                                       |                                                                                                         |                               |
-|                                       | See also :ref:`credentials_retrieval`.                                                                  |                               |
+|                                       | See also :ref:`cps_common_credentials_retrieval`.                                                       |                               |
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
 | config.dmiPluginUserName              | User name used by cps-core to authenticate themselves for using ncmp-dmi-plugin service.                | ``dmiuser``                   |
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
@@ -185,7 +185,7 @@ exhaustive.
 |                                       |                                                                                                         |                               |
 |                                       | If not defined, the password is generated when deploying the application.                               |                               |
 |                                       |                                                                                                         |                               |
-|                                       | See also :ref:`credentials_retrieval`.                                                                  |                               |
+|                                       | See also :ref:`cps_common_credentials_retrieval`.                                                       |                               |
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
 | postgres.config.pgUserName            | Internal user name used by cps-core to connect to its own database.                                     | ``cps``                       |
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
@@ -193,7 +193,7 @@ exhaustive.
 |                                       |                                                                                                         |                               |
 |                                       | If not defined, the password is generated when deploying the application.                               |                               |
 |                                       |                                                                                                         |                               |
-|                                       | See also :ref:`credentials_retrieval`.                                                                  |                               |
+|                                       | See also :ref:`cps_common_credentials_retrieval`.                                                       |                               |
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
 | postgres.config.pgDatabase            | Database name used by cps-core                                                                          | ``cpsdb``                     |
 |                                       |                                                                                                         |                               |
index 02836c5..eb5f6b6 100755 (executable)
@@ -64,7 +64,7 @@ CPS Core uses API's from the following ONAP components
 
 * DMI-Plugin: REST based interface which is used to provide integration
   and allow the DMI registry API's have access to the corresponding NCMP API's within CPS Core.
-  More information on the DMI-Plugins offered APIs can be found on the `DMI-Plugin's Design Page <https://docs.onap.org/projects/onap-cps-ncmp-dmi-plugin/en/latest/design.html>`_.
+  More information on the DMI-Plugins offered APIs can be found on the :ref:`DMI-Plugin's Design Page <onap-cps-ncmp-dmi-plugin:design>`.
 
 CPS Path
 ========
index 5504bf3..b750c6d 100644 (file)
@@ -54,7 +54,11 @@ Data
 
 Querying
 
-- **CPS Path** is used to query data nodes. The CPS Path is described in detail in :doc:`cps-path`.
+- **CPS Path** is used to query data nodes.
+.. toctree::
+   :maxdepth: 1
+
+   cps-path.rst
 
 .. Below Label is used by documentation for other CPS components to link here, do not remove even if it gives a warning
 .. _cps_ncmp_modelling: