Allow limiting results in queryDataLeaf 01/140401/2
authordanielhanrahan <daniel.hanrahan@est.tech>
Wed, 5 Mar 2025 17:28:07 +0000 (17:28 +0000)
committerdanielhanrahan <daniel.hanrahan@est.tech>
Tue, 11 Mar 2025 09:33:56 +0000 (09:33 +0000)
This exposes queryResultLimit parameter in queryDataLeaf,
same as was implemented for queryDataNodes API.

Issue-ID: CPS-2680
Signed-off-by: danielhanrahan <daniel.hanrahan@est.tech>
Change-Id: Ieb922ac1acc91dbfd67fb5ade7856213a2f93ce8

cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java
cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java
cps-service/src/main/java/org/onap/cps/impl/CpsQueryServiceImpl.java
cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java
cps-service/src/test/groovy/org/onap/cps/impl/CpsQueryServiceImplSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/QueryServiceIntegrationSpec.groovy

index e102765..a510d30 100644 (file)
@@ -242,7 +242,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
     @Override
     public <T> Set<T> queryDataLeaf(final String dataspaceName, final String anchorName, final String cpsPath,
-                                    final Class<T> targetClass) {
+                                    final int queryResultLimit, final Class<T> targetClass) {
         final CpsPathQuery cpsPathQuery = getCpsPathQuery(cpsPath);
         if (!cpsPathQuery.hasAttributeAxis()) {
             throw new IllegalArgumentException(
@@ -251,7 +251,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
         final String attributeName = cpsPathQuery.getAttributeAxisAttributeName();
         final List<DataNode> dataNodes = queryDataNodes(dataspaceName, anchorName, cpsPath,
-                FetchDescendantsOption.OMIT_DESCENDANTS);
+                FetchDescendantsOption.OMIT_DESCENDANTS, queryResultLimit);
         return dataNodes.stream()
                 .map(dataNode -> {
                     final Object attributeValue = dataNode.getLeaves().get(attributeName);
index 30c8bbb..d6c1f7f 100644 (file)
@@ -73,6 +73,19 @@ public interface CpsQueryService {
      */
     <T> Set<T> queryDataLeaf(String dataspaceName, String anchorName, String cpsPath, Class<T> targetClass);
 
+    /**
+     * Get data leaf for the given dataspace and anchor by cps path.
+     *
+     * @param dataspaceName      dataspace name
+     * @param anchorName         anchor name
+     * @param cpsPath            cps path
+     * @param queryResultLimit   the maximum number of data nodes to return; if less than 1, returns all matching nodes
+     * @param targetClass        class of the expected data type
+     * @return a collection of data objects of expected type
+     */
+    <T> Set<T> queryDataLeaf(String dataspaceName, String anchorName, String cpsPath, int queryResultLimit,
+                             Class<T> targetClass);
+
     /**
      * Get data nodes for the given dataspace across all anchors by cps path.
      *
index a388482..5abdd0f 100644 (file)
@@ -67,8 +67,15 @@ public class CpsQueryServiceImpl implements CpsQueryService {
     @Override
     public <T> Set<T> queryDataLeaf(final String dataspaceName, final String anchorName, final String cpsPath,
                                     final Class<T> targetClass) {
+        return queryDataLeaf(dataspaceName, anchorName, cpsPath, NO_LIMIT, targetClass);
+    }
+
+    @Override
+    public <T> Set<T> queryDataLeaf(final String dataspaceName, final String anchorName, final String cpsPath,
+                                    final int queryResultLimit, final Class<T> targetClass) {
         cpsValidator.validateNameCharacters(dataspaceName, anchorName);
-        return cpsDataPersistenceService.queryDataLeaf(dataspaceName, anchorName, cpsPath, targetClass);
+        return cpsDataPersistenceService.queryDataLeaf(dataspaceName, anchorName, cpsPath, queryResultLimit,
+                targetClass);
     }
 
     @Override
index 5be5fb0..b319929 100644 (file)
@@ -208,10 +208,12 @@ public interface CpsDataPersistenceService {
      * @param dataspaceName          dataspace name
      * @param anchorName             anchor name
      * @param cpsPath                cps path
+     * @param queryResultLimit       limits the number of returned entities (if less than 1 returns all)
      * @param targetClass            class of the expected data type
      * @return a collection of data objects of expected type
      */
-    <T> Set<T> queryDataLeaf(String dataspaceName, String anchorName, String cpsPath, Class<T> targetClass);
+    <T> Set<T> queryDataLeaf(String dataspaceName, String anchorName, String cpsPath, int queryResultLimit,
+                             Class<T> targetClass);
 
     /**
      * Get a datanode by dataspace name and cps path across all anchors.
index 9db4aa4..d581727 100644 (file)
@@ -77,8 +77,8 @@ class CpsQueryServiceImplSpec extends Specification {
         and: 'the CpsValidator is called on the dataspaceName, schemaSetName and anchorName'
             1 * mockCpsValidator.validateNameCharacters(dataspaceName)
         where: 'all fetch descendants options are supported'
-        fetchDescendantsOption << [FetchDescendantsOption.OMIT_DESCENDANTS, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS,
-                                   FetchDescendantsOption.DIRECT_CHILDREN_ONLY, new FetchDescendantsOption(10)]
+            fetchDescendantsOption << [FetchDescendantsOption.OMIT_DESCENDANTS, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS,
+                                       FetchDescendantsOption.DIRECT_CHILDREN_ONLY, new FetchDescendantsOption(10)]
     }
 
     def 'Query total anchors for dataspace and cps path.'() {
@@ -91,7 +91,7 @@ class CpsQueryServiceImplSpec extends Specification {
     def 'Query data leaf.'() {
         when: 'a query for a specific leaf is executed'
             objectUnderTest.queryDataLeaf('some-dataspace', 'some-anchor', '/cps-path/@id', Object.class)
-        then: 'solution is not implemented yet'
-            1 * mockCpsDataPersistenceService.queryDataLeaf('some-dataspace', 'some-anchor', '/cps-path/@id', Object.class)
+        then: 'the persistence service is called once with the correct parameters'
+            1 * mockCpsDataPersistenceService.queryDataLeaf('some-dataspace', 'some-anchor', '/cps-path/@id', 0, Object.class)
     }
 }
index 42fb964..6ae14dd 100644 (file)
@@ -458,8 +458,8 @@ class QueryServiceIntegrationSpec extends FunctionalSpecBase {
             assert result.anchorName.toSet() == [BOOKSTORE_ANCHOR_1, BOOKSTORE_ANCHOR_2].toSet()
     }
 
-    def 'Query with a limit of #limit.' () {
-        when:
+    def 'Query data nodes with a limit of #limit.' () {
+        when: 'a query for data nodes is executed with a result limit'
             def result = objectUnderTest.queryDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories', OMIT_DESCENDANTS, limit)
         then: 'the expected number of nodes is returned'
             assert countDataNodesInTree(result) == expectedNumberOfResults
@@ -470,4 +470,17 @@ class QueryServiceIntegrationSpec extends FunctionalSpecBase {
             0     || 5
             -1    || 5
     }
+
+    def 'Query data leaf with a limit of #limit.' () {
+        when: 'a query for data leaf is executed with a result limit'
+            def result = objectUnderTest.queryDataLeaf(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories/@name', limit, String)
+        then: 'the expected number of leaf values is returned'
+            assert result.size() == expectedNumberOfResults
+        where: 'the following parameters are used'
+            limit || expectedNumberOfResults
+            1     || 1
+            2     || 2
+            0     || 5
+            -1    || 5
+    }
 }