CPS-265 - updating cps path to support include-descendants option. 58/118658/6
authorniamhcore <niamh.core@est.tech>
Mon, 1 Mar 2021 13:25:13 +0000 (13:25 +0000)
committerNiamh Core <niamh.core@est.tech>
Tue, 2 Mar 2021 11:13:25 +0000 (11:13 +0000)
Issue-ID: CPS-265

Signed-off-by: niamhcore <niamh.core@est.tech>
Change-Id: I9e9b84760dbc8b5eb4b31ab972fdb2d186c6bb48

17 files changed:
cps-nf-proxy-rest/docs/openapi/xnfProxy.yml
cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java
cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy
cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java
cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java
cps-nf-proxy-service/src/test/groovy/org/onap/cps/api/impl/NfProxyDataServiceImplSpec.groovy
cps-rest/docs/api/swagger/cpsQuery.yml
cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java
cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
cps-ri/src/test/resources/data/fragment.sql
cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java
cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy
docs/api/swagger/openapi.yml

index c39d2df..141e472 100644 (file)
@@ -33,6 +33,7 @@ nodesByCmHandleAndCpsPath:
     parameters:
       - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
       - $ref: 'components.yaml#/components/parameters/cpsPathInQuery'
+      - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery'
     responses:
       200:
         $ref: 'components.yaml#/components/responses/Ok'
index 93ed065..680ca12 100644 (file)
@@ -60,8 +60,12 @@ public class NfProxyController implements NfProxyApi {
     }
 
     @Override
-    public ResponseEntity<Object> queryNodesByCmHandleAndCpsPath(final String cmHandle, @Valid final String cpsPath) {
-        final Collection<DataNode> dataNodes = nfProxyDataService.queryDataNodes(cmHandle, cpsPath);
+    public ResponseEntity<Object> queryNodesByCmHandleAndCpsPath(final String cmHandle, @Valid final String cpsPath,
+        @Valid final Boolean includeDescendants) {
+        final FetchDescendantsOption fetchDescendantsOption = Boolean.TRUE.equals(includeDescendants)
+            ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS : FetchDescendantsOption.OMIT_DESCENDANTS;
+        final Collection<DataNode> dataNodes =
+            nfProxyDataService.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption);
         return new ResponseEntity<>(GSON.toJson(dataNodes), HttpStatus.OK);
     }
 
index 742a643..a81411c 100644 (file)
 
 package org.onap.cps.nfproxy.rest.controller
 
-import com.google.common.collect.ImmutableMap
+
 import com.google.gson.Gson
 import org.onap.cps.nfproxy.api.NfProxyDataService
-import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.DataNodeBuilder
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
@@ -34,7 +33,9 @@ import org.springframework.http.HttpStatus
 import org.springframework.http.MediaType
 import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Specification
+import spock.lang.Unroll
 
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
 
@@ -59,22 +60,28 @@ class NfProxyControllerSpec extends Specification {
     def cmHandle = 'some handle'
     def xpath = 'some xpath'
 
-    def 'Query data node by cps path for the given cm handle.'() {
+    @Unroll
+    def 'Query data node by cps path for the given cm handle with #scenario.'() {
         given: 'service method returns a list containing a data node'
-            def cpsPath = '/xpath/leaves[@leaf=\'value\']'
-            def dataNode = new DataNodeBuilder().withXpath("/xpath")
-                    .withLeaves(ImmutableMap.of("leaf", "value")).build()
-            ArrayList<DataNode> dataNodeList = new ArrayList();
-            dataNodeList.add(dataNode)
-            mockNfProxyDataService.queryDataNodes(cmHandle, cpsPath) >> dataNodeList
+            def dataNode = new DataNodeBuilder().withXpath('/xpath').build()
+            def cpsPath = 'some cps-path'
+            mockNfProxyDataService.queryDataNodes(cmHandle, cpsPath, expectedCpsDataServiceOption) >> [dataNode]
         and: 'the query endpoint'
             def dataNodeEndpoint = "$dataNodeBaseEndpoint/cm-handles/$cmHandle/nodes/query"
         when: 'query data nodes API is invoked'
-            def response = mvc.perform(get(dataNodeEndpoint).param('cps-path', cpsPath)).andReturn().response
+            def response = mvc.perform(get(dataNodeEndpoint)
+                    .param('cps-path', cpsPath)
+                    .param('include-descendants', includeDescendantsOption))
+                    .andReturn().response
         then: 'the response contains the the datanode in json format'
             response.status == HttpStatus.OK.value()
             def expectedJsonContent = new Gson().toJson(dataNode)
             response.getContentAsString().contains(expectedJsonContent)
+        where: 'the following options for include descendants are provided in the request'
+            scenario                   | includeDescendantsOption || expectedCpsDataServiceOption
+            'no descendants by default'| ''                       || OMIT_DESCENDANTS
+            'no descendant explicitly' | 'false'                  || OMIT_DESCENDANTS
+            'descendants'              | 'true'                   || INCLUDE_ALL_DESCENDANTS
     }
 
     def 'Update data node leaves.'() {
index ce47d70..cde1801 100644 (file)
@@ -46,12 +46,15 @@ public interface NfProxyDataService {
     /**
      * Get datanodes for the given cm handle by cps path.
      *
-     * @param cmHandle The identifier for a network function, network element, subnetwork or any other cm object by
-     *                 managed NF-Proxy
-     * @param cpsPath  cps path
+     * @param cmHandle               The identifier for a network function, network element, subnetwork or any other cm
+     *                               object by managed NF-Proxy
+     * @param cpsPath                cps path
+     * @param fetchDescendantsOption defines whether the descendants of the node(s) found by the query should be
+     *                               included in the output
      * @return a collection of datanodes
      */
-    Collection<DataNode> queryDataNodes(@NonNull String cmHandle, @NonNull String cpsPath);
+    Collection<DataNode> queryDataNodes(@NonNull String cmHandle, @NonNull String cpsPath,
+        @NonNull FetchDescendantsOption fetchDescendantsOption);
 
     /**
      * Updates data node for given cm handle using xpath to parent node.
index bb15591..cff92fe 100755 (executable)
@@ -47,8 +47,9 @@ public class NfProxyDataServiceImpl implements NfProxyDataService {
     }
 
     @Override
-    public Collection<DataNode> queryDataNodes(final String cmHandle, final String cpsPath) {
-        return cpsQueryService.queryDataNodes(NF_PROXY_DATASPACE_NAME, cmHandle, cpsPath);
+    public Collection<DataNode> queryDataNodes(final String cmHandle, final String cpsPath,
+        final FetchDescendantsOption fetchDescendantsOption) {
+        return cpsQueryService.queryDataNodes(NF_PROXY_DATASPACE_NAME, cmHandle, cpsPath, fetchDescendantsOption);
     }
 
     @Override
index f89f80e..24549ae 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.api.impl
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsQueryService
 import org.onap.cps.nfproxy.api.impl.NfProxyDataServiceImpl
+import org.onap.cps.spi.FetchDescendantsOption
 import spock.lang.Specification
 
 class NfProxyDataServiceImplSpec extends Specification {
@@ -37,13 +38,15 @@ class NfProxyDataServiceImplSpec extends Specification {
     def cmHandle = 'some handle'
     def expectedDataspaceName = 'NFP-Operational'
 
-    def 'Query data nodes by cps path.'() {
+    def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
         given: 'a cm Handle and a cps path'
             def cpsPath = '/cps-path'
         when: 'queryDataNodes is invoked'
-            objectUnderTest.queryDataNodes(cmHandle, cpsPath)
+            objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
         then: 'the persistence service is called once with the correct parameters'
-            1 * mockcpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath)
+            1 * mockcpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
+        where: 'all fetch descendants options are supported'
+            fetchDescendantsOption << FetchDescendantsOption.values()
     }
 
     def 'Update data node leaves.'() {
index 91a4bdb..779c9a0 100644 (file)
@@ -9,6 +9,7 @@ nodesByDataspaceAndAnchorAndCpsPath:
       - $ref: 'components.yml#/components/parameters/dataspaceNameInPath'
       - $ref: 'components.yml#/components/parameters/anchorNameInPath'
       - $ref: 'components.yml#/components/parameters/cpsPathInQuery'
+      - $ref: 'components.yml#/components/parameters/includeDescendantsOptionInQuery'
     responses:
       '200':
         $ref: 'components.yml#/components/responses/Ok'
index a8816f0..c6b5284 100644 (file)
@@ -24,6 +24,7 @@ import java.util.Collection;
 import javax.validation.Valid;
 import org.onap.cps.api.CpsQueryService;
 import org.onap.cps.rest.api.CpsQueryApi;
+import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
@@ -40,9 +41,11 @@ public class QueryRestController implements CpsQueryApi {
 
     @Override
     public ResponseEntity<Object> getNodesByDataspaceAndAnchorAndCpsPath(final String dataspaceName,
-        final String anchorName, @Valid final String cpsPath) {
+        final String anchorName, @Valid final String cpsPath, @Valid final Boolean includeDescendants) {
+        final FetchDescendantsOption fetchDescendantsOption = Boolean.TRUE.equals(includeDescendants)
+            ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS : FetchDescendantsOption.OMIT_DESCENDANTS;
         final Collection<DataNode> dataNodes =
-            cpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath);
+            cpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption);
         return new ResponseEntity<>(new Gson().toJson(dataNodes), HttpStatus.OK);
     }
 }
index 4d9a558..0927c9d 100644 (file)
@@ -19,7 +19,7 @@
 
 package org.onap.cps.rest.controller
 
-import com.google.common.collect.ImmutableMap
+
 import com.google.gson.Gson
 import org.modelmapper.ModelMapper
 import org.onap.cps.api.CpsAdminService
@@ -36,7 +36,10 @@ import org.springframework.http.HttpStatus
 import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Shared
 import spock.lang.Specification
+import spock.lang.Unroll
 
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
 
 @WebMvcTest
@@ -63,22 +66,28 @@ class QueryRestControllerSpec extends Specification {
     @Value('${rest.api.cps-base-path}')
     def basePath
 
-    def 'Query data node by cps path for the given dataspace and anchor.'() {
+    @Unroll
+    def 'Query data node by cps path for the given dataspace and anchor with #scenario.'() {
         given: 'service method returns a list containing a data node'
+            def dataNode = new DataNodeBuilder().withXpath('/xpath').build()
             def dataspaceName = 'my_dataspace'
             def anchorName = 'my_anchor'
-            def cpsPath = '/xpath/leaves[@leaf=\'value\']'
-            def dataNode = new DataNodeBuilder().withXpath("/xpath")
-                    .withLeaves(ImmutableMap.of("leaf", "value")).build()
-            ArrayList<DataNode> dataNodeList = new ArrayList();
-            dataNodeList.add(dataNode)
-            mockCpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath) >> dataNodeList
+            def cpsPath = 'some cps-path'
+            mockCpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, expectedCpsDataServiceOption) >> [dataNode]
         and: 'the query endpoint'
             def dataNodeEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors/$anchorName/nodes/query"
         when: 'query data nodes API is invoked'
-            def response = mvc.perform(get(dataNodeEndpoint).param('cps-path', cpsPath)).andReturn().response
+            def response = mvc.perform(get(dataNodeEndpoint)
+                    .param('cps-path', cpsPath)
+                    .param('include-descendants', includeDescendantsOption))
+                    .andReturn().response
         then: 'the response contains the the datanode in json format'
             response.status == HttpStatus.OK.value()
             response.getContentAsString().contains(new Gson().toJson(dataNode))
+        where: 'the following options for include descendants are provided in the request'
+            scenario                    | includeDescendantsOption || expectedCpsDataServiceOption
+            'no descendants by default' | ''                       || OMIT_DESCENDANTS
+            'no descendant explicitly'  | 'false'                  || OMIT_DESCENDANTS
+            'descendants'               | 'true'                   || INCLUDE_ALL_DESCENDANTS
     }
 }
\ No newline at end of file
index 2d9588e..0c61c99 100644 (file)
@@ -21,7 +21,6 @@
 package org.onap.cps.spi.impl;
 
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
-import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
@@ -127,7 +126,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     }
 
     @Override
-    public List<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath) {
+    public List<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath,
+        final FetchDescendantsOption fetchDescendantsOption) {
         final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
         final CpsPathQuery cpsPathQuery = CpsPathQuery.createFrom(cpsPath);
@@ -135,7 +135,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             .getByAnchorAndXpathAndLeafAttributes(anchorEntity.getId(), cpsPathQuery
                 .getXpathPrefix(), cpsPathQuery.getLeafName(), cpsPathQuery.getLeafValue());
         return fragmentEntities.stream()
-            .map(fragmentEntity -> toDataNode(fragmentEntity, OMIT_DESCENDANTS))
+            .map(fragmentEntity -> toDataNode(fragmentEntity, fetchDescendantsOption))
             .collect(Collectors.toUnmodifiableList());
     }
 
index 0158938..8001c65 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableSet
 import com.google.gson.Gson
 import com.google.gson.GsonBuilder
 import org.onap.cps.spi.CpsDataPersistenceService
+import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.entities.FragmentEntity
 import org.onap.cps.spi.exceptions.AnchorNotFoundException
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
@@ -309,28 +310,28 @@ class CpsDataPersistenceServiceSpec extends CpsPersistenceSpecBase {
     @Sql([CLEAR_DATA, SET_DATA])
     def 'Cps Path query for single leaf value with type: #type.'() {
         when: 'a query is executed to get a data node by the given cps path'
-            def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, cpsPath)
+            def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, cpsPath, includeDescendantsOption)
         then: 'the correct data is returned'
             def leaves ='[common-leaf-name:common-leaf-value, common-leaf-name-int:5.0]'
-            result.size() == 1
-            def dataNode = result.stream().findFirst().get()
+            DataNode dataNode = result.stream().findFirst().get()
             dataNode.getLeaves().toString() == leaves
+            dataNode.getChildDataNodes().size() == expectedNumberOfChidlNodes
         where: 'the following data is used'
-            type      | cpsPath
-            'String'  | '/parent-200/child-202[@common-leaf-name=\'common-leaf-value\']'
-            'Integer' | '/parent-200/child-202[@common-leaf-name-int=5]'
+            type                         | cpsPath                                                          | includeDescendantsOption | expectedNumberOfChidlNodes
+            'String and no descendants'  | '/parent-200/child-202[@common-leaf-name=\'common-leaf-value\']' | OMIT_DESCENDANTS         | 0
+            'Integer and descendants'    | '/parent-200/child-202[@common-leaf-name-int=5]'                 | INCLUDE_ALL_DESCENDANTS  | 1
     }
 
     @Unroll
     @Sql([CLEAR_DATA, SET_DATA])
     def 'Query for attribute by cps path with cps paths that return no data because of #scenario.'() {
         when: 'a query is executed to get datanodes for the given cps path'
-            def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, cpsPath)
+            def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, cpsPath, FetchDescendantsOption.OMIT_DESCENDANTS)
         then: 'no data is returned'
             result.isEmpty()
         where: 'following cps queries are performed'
             scenario                         | cpsPath
-            'cps path is incomplete'               | '/parent-200[@common-leaf-name-int=5]'
+            'cps path is incomplete'         | '/parent-200[@common-leaf-name-int=5]'
             'missing / at beginning of path' | 'parent-200/child-202[@common-leaf-name-int=5]'
             'leaf value does not exist'      | '/parent-200/child-202[@common-leaf-name=\'does not exist\']'
             'incomplete end of xpath prefix' | '/parent-200/child-20[@common-leaf-name-int=5]'
index 4b50578..3c1f793 100644 (file)
@@ -26,4 +26,5 @@ INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES)
     (4201, 1001, 3003, null, '/parent-200', '{"leaf-value": "original"}'),
     (4202, 1001, 3003, 4201, '/parent-200/child-201', '{"leaf-value": "original"}'),
     (4203, 1001, 3003, 4202, '/parent-200/child-201/grand-child', '{"leaf-value": "original"}'),
-    (4204, 1001, 3003, 4201, '/parent-200/child-202', '{"common-leaf-name": "common-leaf-value", "common-leaf-name-int" : 5}');
\ No newline at end of file
+    (4204, 1001, 3003, 4201, '/parent-200/child-202', '{"common-leaf-name": "common-leaf-value", "common-leaf-name-int" : 5}'),
+    (4205, 1001, 3003, 4204, '/parent-200/child-202/grand-child-202', '{"common-leaf-name": "common-leaf-value", "common-leaf-name-int" : 5}');
\ No newline at end of file
index b432af8..0f4bf2d 100644 (file)
@@ -21,6 +21,7 @@ package org.onap.cps.api;
 
 import java.util.Collection;
 import org.checkerframework.checker.nullness.qual.NonNull;
+import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
 
 /*
@@ -34,9 +35,11 @@ public interface CpsQueryService {
      * @param dataspaceName          dataspace name
      * @param anchorName             anchor name
      * @param cpsPath                cps path
+     * @param fetchDescendantsOption defines whether the descendants of the node(s) found by the query should be
+     *                               included in the output
      * @return a collection of data nodes
      */
     Collection<DataNode> queryDataNodes(@NonNull String dataspaceName, @NonNull String anchorName,
-        @NonNull String cpsPath);
+        @NonNull String cpsPath, @NonNull FetchDescendantsOption fetchDescendantsOption);
 
 }
index 63d0a0f..79fa6c7 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.api.impl;
 import java.util.Collection;
 import org.onap.cps.api.CpsQueryService;
 import org.onap.cps.spi.CpsDataPersistenceService;
+import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -34,7 +35,7 @@ public class CpsQueryServiceImpl implements CpsQueryService {
 
     @Override
     public Collection<DataNode> queryDataNodes(final String dataspaceName, final String anchorName,
-        final String cpsPath) {
-        return cpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath);
+        final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) {
+        return cpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption);
     }
 }
\ No newline at end of file
index d2b6d45..48f9763 100644 (file)
@@ -94,8 +94,10 @@ public interface CpsDataPersistenceService {
      * @param dataspaceName          dataspace name
      * @param anchorName             anchor name
      * @param cpsPath                cps path
+     * @param fetchDescendantsOption defines whether the descendants of the node(s) found by the query should be
+     *                               included in the output
      * @return the data nodes found i.e. 0 or more data nodes
      */
     Collection<DataNode> queryDataNodes(@NonNull String dataspaceName, @NonNull String anchorName,
-        @NonNull String cpsPath);
+        @NonNull String cpsPath, @NonNull FetchDescendantsOption fetchDescendantsOption);
 }
index 6e044b0..99d25ec 100644 (file)
 package org.onap.cps.api.impl
 
 import org.onap.cps.spi.CpsDataPersistenceService
+import org.onap.cps.spi.FetchDescendantsOption
 import spock.lang.Specification
+import spock.lang.Unroll
+
 
 class CpsQueryServiceImplSpec extends Specification {
     def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
@@ -31,14 +34,17 @@ class CpsQueryServiceImplSpec extends Specification {
         objectUnderTest.cpsDataPersistenceService = mockCpsDataPersistenceService
     }
 
-    def 'Query data nodes by cps path.'() {
+    @Unroll
+    def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
         given: 'a dataspace name, an anchor name and a cps path'
             def dataspaceName = 'some dataspace'
             def anchorName = 'some anchor'
             def cpsPath = '/cps-path'
         when: 'queryDataNodes is invoked'
-            objectUnderTest.queryDataNodes(dataspaceName, anchorName, cpsPath)
+            objectUnderTest.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption)
         then: 'the persistence service is called once with the correct parameters'
-            1 * mockCpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath)
+            1 * mockCpsDataPersistenceService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption)
+        where: 'all fetch descendants options are supported'
+            fetchDescendantsOption << FetchDescendantsOption.values()
     }
 }
\ No newline at end of file
index f827e45..d158561 100755 (executable)
@@ -764,6 +764,13 @@ paths:
           schema:
             type: string
             default: /
+        - name: include-descendants
+          in: query
+          description: include-descendants
+          required: false
+          schema:
+            type: boolean
+            default: false
       responses:
         '200':
           description: OK