From 32446dce35b5bf9d2c84751718cb4ece7f96fa72 Mon Sep 17 00:00:00 2001 From: niamhcore Date: Mon, 1 Mar 2021 13:25:13 +0000 Subject: [PATCH] CPS-265 - updating cps path to support include-descendants option. Issue-ID: CPS-265 Signed-off-by: niamhcore Change-Id: I9e9b84760dbc8b5eb4b31ab972fdb2d186c6bb48 --- cps-nf-proxy-rest/docs/openapi/xnfProxy.yml | 1 + .../nfproxy/rest/controller/NfProxyController.java | 8 +++++-- .../rest/controller/NfProxyControllerSpec.groovy | 27 ++++++++++++++-------- .../onap/cps/nfproxy/api/NfProxyDataService.java | 11 +++++---- .../nfproxy/api/impl/NfProxyDataServiceImpl.java | 5 ++-- .../cps/api/impl/NfProxyDataServiceImplSpec.groovy | 9 +++++--- cps-rest/docs/api/swagger/cpsQuery.yml | 1 + .../cps/rest/controller/QueryRestController.java | 7 ++++-- .../rest/controller/QueryRestControllerSpec.groovy | 27 ++++++++++++++-------- .../spi/impl/CpsDataPersistenceServiceImpl.java | 6 ++--- .../spi/impl/CpsDataPersistenceServiceSpec.groovy | 17 +++++++------- cps-ri/src/test/resources/data/fragment.sql | 3 ++- .../java/org/onap/cps/api/CpsQueryService.java | 5 +++- .../org/onap/cps/api/impl/CpsQueryServiceImpl.java | 5 ++-- .../onap/cps/spi/CpsDataPersistenceService.java | 4 +++- .../cps/api/impl/CpsQueryServiceImplSpec.groovy | 12 +++++++--- docs/api/swagger/openapi.yml | 7 ++++++ 17 files changed, 104 insertions(+), 51 deletions(-) diff --git a/cps-nf-proxy-rest/docs/openapi/xnfProxy.yml b/cps-nf-proxy-rest/docs/openapi/xnfProxy.yml index c39d2dff0..141e47258 100644 --- a/cps-nf-proxy-rest/docs/openapi/xnfProxy.yml +++ b/cps-nf-proxy-rest/docs/openapi/xnfProxy.yml @@ -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' diff --git a/cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java b/cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java index 93ed06580..680ca127b 100644 --- a/cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java +++ b/cps-nf-proxy-rest/src/main/java/org/onap/cps/nfproxy/rest/controller/NfProxyController.java @@ -60,8 +60,12 @@ public class NfProxyController implements NfProxyApi { } @Override - public ResponseEntity queryNodesByCmHandleAndCpsPath(final String cmHandle, @Valid final String cpsPath) { - final Collection dataNodes = nfProxyDataService.queryDataNodes(cmHandle, cpsPath); + public ResponseEntity 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 dataNodes = + nfProxyDataService.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption); return new ResponseEntity<>(GSON.toJson(dataNodes), HttpStatus.OK); } diff --git a/cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy b/cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy index 742a643fa..a81411caf 100644 --- a/cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy +++ b/cps-nf-proxy-rest/src/test/groovy/org/onap/cps/nfproxy/rest/controller/NfProxyControllerSpec.groovy @@ -21,10 +21,9 @@ 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 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.'() { diff --git a/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java b/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java index ce47d7001..cde1801da 100644 --- a/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java +++ b/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/NfProxyDataService.java @@ -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 queryDataNodes(@NonNull String cmHandle, @NonNull String cpsPath); + Collection queryDataNodes(@NonNull String cmHandle, @NonNull String cpsPath, + @NonNull FetchDescendantsOption fetchDescendantsOption); /** * Updates data node for given cm handle using xpath to parent node. diff --git a/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java b/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java index bb15591a9..cff92fea6 100755 --- a/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java +++ b/cps-nf-proxy-service/src/main/java/org/onap/cps/nfproxy/api/impl/NfProxyDataServiceImpl.java @@ -47,8 +47,9 @@ public class NfProxyDataServiceImpl implements NfProxyDataService { } @Override - public Collection queryDataNodes(final String cmHandle, final String cpsPath) { - return cpsQueryService.queryDataNodes(NF_PROXY_DATASPACE_NAME, cmHandle, cpsPath); + public Collection queryDataNodes(final String cmHandle, final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption) { + return cpsQueryService.queryDataNodes(NF_PROXY_DATASPACE_NAME, cmHandle, cpsPath, fetchDescendantsOption); } @Override diff --git a/cps-nf-proxy-service/src/test/groovy/org/onap/cps/api/impl/NfProxyDataServiceImplSpec.groovy b/cps-nf-proxy-service/src/test/groovy/org/onap/cps/api/impl/NfProxyDataServiceImplSpec.groovy index f89f80e30..24549aec8 100644 --- a/cps-nf-proxy-service/src/test/groovy/org/onap/cps/api/impl/NfProxyDataServiceImplSpec.groovy +++ b/cps-nf-proxy-service/src/test/groovy/org/onap/cps/api/impl/NfProxyDataServiceImplSpec.groovy @@ -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.'() { diff --git a/cps-rest/docs/api/swagger/cpsQuery.yml b/cps-rest/docs/api/swagger/cpsQuery.yml index 91a4bdbfa..779c9a094 100644 --- a/cps-rest/docs/api/swagger/cpsQuery.yml +++ b/cps-rest/docs/api/swagger/cpsQuery.yml @@ -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' diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java index a8816f02b..c6b5284fb 100644 --- a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java @@ -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 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 dataNodes = - cpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath); + cpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption); return new ResponseEntity<>(new Gson().toJson(dataNodes), HttpStatus.OK); } } diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy index 4d9a558ef..0927c9d1e 100644 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy @@ -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 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 diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index 2d9588e8f..0c61c9909 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -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 queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath) { + public List 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()); } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy index 015893817..8001c659f 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy @@ -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]' diff --git a/cps-ri/src/test/resources/data/fragment.sql b/cps-ri/src/test/resources/data/fragment.sql index 4b5057807..3c1f793b9 100644 --- a/cps-ri/src/test/resources/data/fragment.sql +++ b/cps-ri/src/test/resources/data/fragment.sql @@ -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 diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java b/cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java index b432af800..0f4bf2dc4 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsQueryService.java @@ -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 queryDataNodes(@NonNull String dataspaceName, @NonNull String anchorName, - @NonNull String cpsPath); + @NonNull String cpsPath, @NonNull FetchDescendantsOption fetchDescendantsOption); } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java index 63d0a0fbb..79fa6c717 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java @@ -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 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 diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java index d2b6d45d6..48f9763ee 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java @@ -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 queryDataNodes(@NonNull String dataspaceName, @NonNull String anchorName, - @NonNull String cpsPath); + @NonNull String cpsPath, @NonNull FetchDescendantsOption fetchDescendantsOption); } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy index 6e044b044..99d25ecfc 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy @@ -20,7 +20,10 @@ 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 diff --git a/docs/api/swagger/openapi.yml b/docs/api/swagger/openapi.yml index f827e4585..d158561be 100755 --- a/docs/api/swagger/openapi.yml +++ b/docs/api/swagger/openapi.yml @@ -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 -- 2.16.6