diff options
author | Luke Gleeson <luke.gleeson@est.tech> | 2023-08-03 13:13:47 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2023-08-03 13:13:47 +0000 |
commit | 478c5dac54ed508f0ce97e18e91aac7b821d814f (patch) | |
tree | 6b8d3d25972ebacf2d429ede2fff601cce15bc2e /cps-service/src | |
parent | 2a1e576e6d12456e43c47d4cd81be7f88d1a2a2b (diff) | |
parent | f248b5d9b794d5bdff59145406e0398d6fdcafa4 (diff) |
Merge "Support pagination in query across all anchors(ep4)"
Diffstat (limited to 'cps-service/src')
9 files changed, 166 insertions, 23 deletions
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 af54077fea..edd2d2ad32 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 @@ -23,6 +23,7 @@ package org.onap.cps.api; import java.util.Collection; import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.PaginationOption; import org.onap.cps.spi.model.DataNode; /* @@ -50,8 +51,18 @@ public interface CpsQueryService { * @param cpsPath CPS path * @param fetchDescendantsOption defines whether the descendants of the node(s) found by the query should be * included in the output + * @param paginationOption pagination option * @return a collection of data nodes */ Collection<DataNode> queryDataNodesAcrossAnchors(String dataspaceName, String cpsPath, - FetchDescendantsOption fetchDescendantsOption); + FetchDescendantsOption fetchDescendantsOption, + PaginationOption paginationOption); + + /** + * Query total number of anchors for given dataspace name and cps path. + * @param dataspaceName dataspace name + * @param cpsPath cps path + * @return total number of anchors for given dataspace name and cps path. + */ + Integer countAnchorsForDataspaceAndCpsPath(String dataspaceName, String cpsPath); } 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 ac018c9e8f..1d7a7ceeb0 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 @@ -27,6 +27,7 @@ import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsQueryService; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.PaginationOption; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Service; @@ -49,8 +50,17 @@ public class CpsQueryServiceImpl implements CpsQueryService { @Override public Collection<DataNode> queryDataNodesAcrossAnchors(final String dataspaceName, - final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) { + final String cpsPath, final FetchDescendantsOption fetchDescendantsOption, + final PaginationOption paginationOption) { + cpsValidator.validateNameCharacters(dataspaceName); + cpsValidator.validatePaginationOption(paginationOption); + return cpsDataPersistenceService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath, + fetchDescendantsOption, paginationOption); + } + + @Override + public Integer countAnchorsForDataspaceAndCpsPath(final String dataspaceName, final String cpsPath) { cpsValidator.validateNameCharacters(dataspaceName); - return cpsDataPersistenceService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath, fetchDescendantsOption); + return cpsDataPersistenceService.countAnchorsForDataspaceAndCpsPath(dataspaceName, cpsPath); } } 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 9674bbe8c1..1baca4ea7d 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 @@ -200,11 +200,12 @@ public interface CpsDataPersistenceService { * @param cpsPath cps path * @param fetchDescendantsOption defines whether the descendants of the node(s) found by the query should be * included in the output + * @param paginationOption pagination option * @return the data nodes found i.e. 0 or more data nodes */ List<DataNode> queryDataNodesAcrossAnchors(String dataspaceName, - String cpsPath, FetchDescendantsOption fetchDescendantsOption); - + String cpsPath, FetchDescendantsOption fetchDescendantsOption, + PaginationOption paginationOption); /** * Starts a session which allows use of locks and batch interaction with the persistence service. @@ -230,4 +231,12 @@ public interface CpsDataPersistenceService { * @param timeoutInMilliseconds lock attempt timeout in milliseconds */ void lockAnchor(String sessionID, String dataspaceName, String anchorName, Long timeoutInMilliseconds); + + /** + * Query total anchors for dataspace name and cps path. + * @param dataspaceName datasoace name + * @param cpsPath cps path + * @return total anchors for dataspace name and cps path + */ + Integer countAnchorsForDataspaceAndCpsPath(String dataspaceName, String cpsPath); } diff --git a/cps-service/src/main/java/org/onap/cps/spi/PaginationOption.java b/cps-service/src/main/java/org/onap/cps/spi/PaginationOption.java new file mode 100644 index 0000000000..17f025dba6 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/spi/PaginationOption.java @@ -0,0 +1,38 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * 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.spi; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class PaginationOption { + + private int pageIndex; + + private int pageSize; + + public static final PaginationOption NO_PAGINATION = null; + + public boolean isValidPaginationOption() { + return this.pageIndex > 0 && this.pageSize > 0; + } +} diff --git a/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java b/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java index 231094cf16..ceb75c09b2 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java +++ b/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java @@ -20,6 +20,8 @@ package org.onap.cps.spi.utils; +import org.onap.cps.spi.PaginationOption; + public interface CpsValidator { /** @@ -35,4 +37,11 @@ public interface CpsValidator { * @param names names of data to be validated */ void validateNameCharacters(final Iterable<String> names); + + /** + * Validate pagination option. + * + * @param paginationOption pagination option + */ + void validatePaginationOption(final PaginationOption paginationOption); } diff --git a/cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java b/cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java index b4d5a09447..1ac2bddf88 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java +++ b/cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java @@ -28,8 +28,10 @@ import static java.util.stream.Collectors.toUnmodifiableList; import static java.util.stream.Collectors.toUnmodifiableMap; import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -52,17 +54,29 @@ public class DataMapUtils { } /** - * Converts DataNode structure into a map including the root node identifier for a JSON response. - * - * @param dataNode data node object - * @return a map representing same data with the root node identifier + * Converts list of DataNode structure into a map including the root node identifier for a JSON response. + * @param dataNodeList list of data nodes for a given anchor name + * @param anchorName anchor name + * @param prefix prefix + * @return a map representing same list of data for given anchor with the root node identifier */ - public static Map<String, Object> toDataMapWithIdentifierAndAnchor(final DataNode dataNode, final String prefix) { - final String nodeIdentifierWithPrefix = getNodeIdentifierWithPrefix(dataNode.getXpath(), prefix); - final Map<String, Object> dataMap = ImmutableMap.<String, Object>builder() - .put(nodeIdentifierWithPrefix, toDataMap(dataNode)).build(); - return ImmutableMap.<String, Object>builder().put("anchorName", dataNode.getAnchorName()) - .put("dataNode", dataMap).build(); + public static Map<String, Object> toDataMapWithIdentifierAndAnchor(final List<DataNode> dataNodeList, + final String anchorName, final String prefix) { + final List<Map<String, Object>> dataMaps = toDataNodesWithIdentifier(dataNodeList, prefix); + return ImmutableMap.<String, Object>builder().put("anchorName", anchorName) + .put("dataNodes", dataMaps).build(); + } + + private static List<Map<String, Object>> toDataNodesWithIdentifier(final List<DataNode> dataNodeList, + final String prefix) { + final List<Map<String, Object>> dataMaps = new ArrayList<>(dataNodeList.size()); + for (final DataNode dataNode: dataNodeList) { + final String nodeIdentifierWithPrefix = getNodeIdentifierWithPrefix(dataNode.getXpath(), prefix); + final Map<String, Object> dataMap = ImmutableMap.<String, Object>builder() + .put(nodeIdentifierWithPrefix, toDataMap(dataNode)).build(); + dataMaps.add(dataMap); + } + return dataMaps; } /** 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 553027a4b8..1ad5017919 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 @@ -23,6 +23,7 @@ package org.onap.cps.api.impl import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.FetchDescendantsOption +import org.onap.cps.spi.PaginationOption import org.onap.cps.spi.utils.CpsValidator import spock.lang.Specification @@ -52,14 +53,22 @@ class CpsQueryServiceImplSpec extends Specification { given: 'a dataspace name, an anchor name and a cps path' def dataspaceName = 'some-dataspace' def cpsPath = '/cps-path' + def paginationOption = new PaginationOption(1, 2) when: 'queryDataNodes is invoked' - objectUnderTest.queryDataNodesAcrossAnchors(dataspaceName, cpsPath, fetchDescendantsOption) + objectUnderTest.queryDataNodesAcrossAnchors(dataspaceName, cpsPath, fetchDescendantsOption, paginationOption) then: 'the persistence service is called once with the correct parameters' - 1 * mockCpsDataPersistenceService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath, fetchDescendantsOption) + 1 * mockCpsDataPersistenceService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath, fetchDescendantsOption, paginationOption) 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 << [FetchDescendantsOption.OMIT_DESCENDANTS, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS, + FetchDescendantsOption.DIRECT_CHILDREN_ONLY, new FetchDescendantsOption(10)] } + def 'Query total anchors for dataspace and cps path.'() { + when: 'query total anchors is invoked' + objectUnderTest.countAnchorsForDataspaceAndCpsPath("some-dataspace", "/cps-path") + then: 'the persistence service is called once with the correct parameters' + 1 * mockCpsDataPersistenceService.countAnchorsForDataspaceAndCpsPath("some-dataspace", "/cps-path") + } } diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/PaginationOptionSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/PaginationOptionSpec.groovy new file mode 100644 index 0000000000..9d74a17222 --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/spi/PaginationOptionSpec.groovy @@ -0,0 +1,41 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2023 Nordix Foundation + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * 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.spi + +import spock.lang.Specification + +class PaginationOptionSpec extends Specification { + + def 'Pagination validation with: #scenario'() { + given: 'pagination option with pageIndex and pageSize' + def paginationOption = new PaginationOption(pageIndex, pageSize) + expect: 'validation returns expected result' + assert paginationOption.isValidPaginationOption() == expectedIsValidPaginationOption + where: 'following parameters are used' + scenario | pageIndex | pageSize || expectedIsValidPaginationOption + 'valid pagination' | 1 | 1 || true + 'negative index' | -1 | 1 || false + 'negative size' | 1 | -1 || false + 'zero index' | 0 | 1 || false + 'zero size' | 1 | 0 || false + } +} diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/DataMapUtilsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/DataMapUtilsSpec.groovy index 29085a9c7e..6b9f9acb3f 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/DataMapUtilsSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/DataMapUtilsSpec.groovy @@ -74,17 +74,19 @@ class DataMapUtilsSpec extends Specification { def 'Data node structure with anchor name conversion to map with root node identifier.'() { when: 'data node structure is converted to a map with root node identifier' - def result = DataMapUtils.toDataMapWithIdentifierAndAnchor(dataNodeWithAnchor, dataNodeWithAnchor.moduleNamePrefix) + def result = DataMapUtils.toDataMapWithIdentifierAndAnchor([dataNodeWithAnchor], dataNodeWithAnchor.anchorName, dataNodeWithAnchor.moduleNamePrefix) then: 'root node leaves are populated under its node identifier' - def parentNode = result.get("dataNode").parent - parentNode.parentLeaf == 'parentLeafValue' - parentNode.parentLeafList == ['parentLeafListEntry1','parentLeafListEntry2'] + def dataNodes = result.dataNodes as List + assert dataNodes.size() == 1 + def parentNode = dataNodes[0].parent + assert parentNode.parentLeaf == 'parentLeafValue' + assert parentNode.parentLeafList == ['parentLeafListEntry1','parentLeafListEntry2'] and: 'leaves for child element is populated under its node identifier' assert parentNode.'child-object'.childLeaf == 'childLeafValue' and: 'leaves for grandchild element is populated under its node identifier' assert parentNode.'child-object'.'grand-child-object'.grandChildLeaf == 'grandChildLeafValue' and: 'data node is associated with anchor name' - assert result.get('anchorName') == 'anchor01' + assert result.anchorName == 'anchor01' } def 'Data node without leaves and without children.'() { |