summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps-bom/pom.xml5
-rw-r--r--cps-ncmp-rest/docs/openapi/components.yaml60
-rwxr-xr-xcps-ncmp-rest/docs/openapi/ncmp.yml26
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java2
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy2
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperTest.groovy2
-rw-r--r--cps-ncmp-service/pom.xml6
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java144
-rwxr-xr-xcps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java4
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueries.java165
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java65
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java25
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy192
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesSpec.groovy150
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy60
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy21
-rw-r--r--cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java36
-rw-r--r--cps-service/src/main/java/org/onap/cps/utils/ValidQueryProperties.java36
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy25
19 files changed, 699 insertions, 327 deletions
diff --git a/cps-bom/pom.xml b/cps-bom/pom.xml
index 9b864b07fe..bf8fbe8c07 100644
--- a/cps-bom/pom.xml
+++ b/cps-bom/pom.xml
@@ -114,6 +114,11 @@
<artifactId>spotbugs</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cps-path-parser</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
</project>
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index 2cb9d894c0..14fd4d24d3 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -185,18 +185,7 @@ components:
type: object
$ref: '#/components/schemas/OldConditionProperties'
description: not necessary, it is just for backward compatibility
- example:
- cmHandleQueryParameters:
- - conditionName: hasAllModules
- conditionParameters:
- - { "moduleName": "my-module-1" }
- - { "moduleName": "my-module-2" }
- - { "moduleName": "my-module-3" }
- - conditionName: hasAllProperties
- conditionParameters:
- - { "Color": "yellow" }
- - { "Shape": "circle" }
- - { "Size": "small" }
+
ConditionProperties:
properties:
conditionName:
@@ -279,7 +268,7 @@ components:
sync-state:
type: object
properties:
- state:
+ syncState:
type: string
example: NONE_REQUESTED
lastSyncTime:
@@ -380,6 +369,51 @@ components:
- Philip Pullman
name: kids
+ allCmHandleQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: hasAllModules
+ conditionParameters:
+ - { "moduleName": "my-module-1" }
+ - { "moduleName": "my-module-2" }
+ - { "moduleName": "my-module-3" }
+ - conditionName: hasAllProperties
+ conditionParameters:
+ - { "Color": "yellow" }
+ - { "Shape": "circle" }
+ - { "Size": "small" }
+ - conditionName: cmHandleWithCpsPath
+ conditionParameters:
+ - { "cpsPath": "//state[@cm-handle-state='ADVISED']" }
+ pubPropCmHandleQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: hasAllProperties
+ conditionParameters:
+ - { "Color": "yellow" }
+ - { "Shape": "circle" }
+ - { "Size": "small" }
+ modulesCmHandleQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: hasAllModules
+ conditionParameters:
+ - { "moduleName": "my-module-1" }
+ - { "moduleName": "my-module-2" }
+ - { "moduleName": "my-module-3" }
+ cpsPathCmHandleStateQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: cmHandleWithCpsPath
+ conditionParameters:
+ - { "cpsPath": "//state[@cm-handle-state='LOCKED']" }
+ cpsPathCmHandleDataSyncQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: cmHandleWithCpsPath
+ conditionParameters:
+ - { "cpsPath": "//state[@data-sync-enabled='true']" }
+
parameters:
cmHandleInPath:
name: cm-handle
diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index aaf0d6a1ab..d7b383705f 100755
--- a/cps-ncmp-rest/docs/openapi/ncmp.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -273,7 +273,7 @@ fetchModuleDefinitionsByCmHandle:
searchCmHandles:
post:
- description: Execute cm handle query search, to be included in the result a cm-handle must fulfill ALL the conditions listed here, if one of the given module names does not exists, return with an empty collection.
+ description: Execute cm handle query search and return a list of cm handle details. Any number of conditions can be applied. To be included in the result a cm-handle must fulfill ALL the conditions. An empty collection will be returned in the case that the cm handle does not match a condition. For more on cm handle query search please refer to <a href="RTD page to be created in separate task">cm handle query search Read the Docs</a>.<br/>By supplying a CPS Path it is possible to query on any data related to the cm handle. For more on CPS Path please refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html">CPS Path Read the Docs</a>. The cm handle ancestor is automatically returned for this query.
tags:
- network-cm-proxy
summary: Execute cm handle search using the available conditions
@@ -284,6 +284,17 @@ searchCmHandles:
application/json:
schema:
$ref: 'components.yaml#/components/schemas/CmHandleQueryParameters'
+ examples:
+ Cm handle properties query:
+ $ref: 'components.yaml#/components/examples/pubPropCmHandleQueryParameters'
+ Cm handle modules query:
+ $ref: 'components.yaml#/components/examples/modulesCmHandleQueryParameters'
+ All cm handle query parameters:
+ $ref: 'components.yaml#/components/examples/allCmHandleQueryParameters'
+ Cm handle with CPS path state query:
+ $ref: 'components.yaml#/components/examples/cpsPathCmHandleStateQueryParameters'
+ Cm handle with data sync flag query:
+ $ref: 'components.yaml#/components/examples/cpsPathCmHandleDataSyncQueryParameters'
responses:
200:
description: OK
@@ -379,7 +390,7 @@ getCmHandleStateById:
searchCmHandleIds:
post:
- description: Execute cm handle query search, to be included in the result a cm-handle must fulfill ALL the conditions listed here, if one of the given module names does not exists, return with an empty collection.
+ description: Execute cm handle query search and return a list of cm handle ids. Any number of conditions can be applied. To be included in the result a cm-handle must fulfill ALL the conditions. An empty collection will be returned in the case that the cm handle does not match a condition. For more on cm handle query search please refer to <a href="RTD page to be created in separate task">cm handle query search Read the Docs</a>.<br/>By supplying a CPS Path it is possible to query on any data related to the cm handle. For more on CPS Path please refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html">CPS Path Read the Docs</a>. The cm handle ancestor is automatically returned for this query.
tags:
- network-cm-proxy
summary: Execute cm handle query upon a given set of query parameters
@@ -390,6 +401,17 @@ searchCmHandleIds:
application/json:
schema:
$ref: 'components.yaml#/components/schemas/CmHandleQueryParameters'
+ examples:
+ Cm handle properties query:
+ $ref: 'components.yaml#/components/examples/pubPropCmHandleQueryParameters'
+ Cm handle modules query:
+ $ref: 'components.yaml#/components/examples/modulesCmHandleQueryParameters'
+ All cm handle query parameters:
+ $ref: 'components.yaml#/components/examples/allCmHandleQueryParameters'
+ Cm handle with CPS path state query:
+ $ref: 'components.yaml#/components/examples/cpsPathCmHandleStateQueryParameters'
+ Cm handle with data sync flag query:
+ $ref: 'components.yaml#/components/examples/cpsPathCmHandleDataSyncQueryParameters'
responses:
200:
description: OK
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java
index 55b64ec760..097dd0af49 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java
@@ -56,7 +56,7 @@ public interface CmHandleStateMapper {
if (compositeStateDataStore.getOperationalDataStore() != null) {
final SyncState operationalSyncState = new SyncState();
- operationalSyncState.setState(compositeStateDataStore.getOperationalDataStore()
+ operationalSyncState.setSyncState(compositeStateDataStore.getOperationalDataStore()
.getDataStoreSyncState().name());
operationalSyncState.setLastSyncTime(compositeStateDataStore.getOperationalDataStore().getLastSyncTime());
dataStores.setOperational(operationalSyncState);
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
index 23263c9aa8..06a7759be9 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
@@ -453,7 +453,7 @@ class NetworkCmProxyControllerSpec extends Specification {
'"dataSyncEnabled":false',
'"dataSyncState":',
'"operational":',
- '"state":"NONE_REQUESTED"',
+ '"syncState":"NONE_REQUESTED"',
'"lastSyncTime":"2022-12-31T20:30:40.000+0000"',
'"running":null'
]
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperTest.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperTest.groovy
index 677cf66127..663b9d02a6 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperTest.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperTest.groovy
@@ -56,7 +56,7 @@ class CmHandleStateMapperTest extends Specification {
assert result.lockReason.reason == 'LOCKED_MISBEHAVING'
assert result.lockReason.details == 'locked details'
assert result.cmHandleState == 'ADVISED'
- assert result.dataSyncState.operational.getState() != null
+ assert result.dataSyncState.operational.getSyncState() != null
}
def 'Internal to External Lock Reason Mapping of #scenario'() {
diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml
index 93c265a7bf..d94c6d1dec 100644
--- a/cps-ncmp-service/pom.xml
+++ b/cps-ncmp-service/pom.xml
@@ -3,7 +3,7 @@
============LICENSE_START=======================================================
Copyright (C) 2021-2022 Nordix Foundation
Modifications Copyright (C) 2021 Pantheon.tech
- Modifications Copyright (C) 2022 Bell Canada
+ Modifications Copyright (C) 2022 Bell Canada
================================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -46,6 +46,10 @@
<artifactId>cps-ncmp-events</artifactId>
</dependency>
<dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cps-path-parser</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java
index a62a009ce7..d784bcdeac 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java
@@ -21,6 +21,8 @@
package org.onap.cps.ncmp.api.impl;
import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle;
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
+import static org.onap.cps.utils.CmHandleQueryRestParametersValidator.validateCpsPathConditionProperties;
import static org.onap.cps.utils.CmHandleQueryRestParametersValidator.validateModuleNameConditionProperties;
import java.util.ArrayList;
@@ -31,17 +33,22 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.cpspath.parser.PathParsingException;
import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
+import org.onap.cps.ncmp.api.inventory.CmHandleQueries;
import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.spi.exceptions.DataValidationException;
import org.onap.cps.spi.model.Anchor;
import org.onap.cps.spi.model.CmHandleQueryServiceParameters;
import org.onap.cps.spi.model.ConditionProperties;
import org.onap.cps.spi.model.DataNode;
+import org.onap.cps.utils.ValidQueryProperties;
import org.springframework.stereotype.Service;
@Service
@@ -49,9 +56,8 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCmHandlerQueryService {
- private static final String PROPERTY_QUERY_NAME = "hasAllProperties";
- private static final String MODULE_QUERY_NAME = "hasAllModules";
- private static final Map<String, NcmpServiceCmHandle> NO_QUERY_EXECUTED = null;
+ private static final Map<String, NcmpServiceCmHandle> NO_QUERY_TO_EXECUTE = null;
+ private final CmHandleQueries cmHandleQueries;
private final InventoryPersistence inventoryPersistence;
/**
@@ -68,14 +74,10 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
return getAllCmHandles();
}
- final Map<String, NcmpServiceCmHandle> publicPropertyQueryResult
- = executePublicPropertyQueries(cmHandleQueryServiceParameters);
+ final Map<String, NcmpServiceCmHandle> combinedQueryResult = executeInventoryQueries(
+ cmHandleQueryServiceParameters);
- final Map<String, NcmpServiceCmHandle> combinedQueryResult =
- combineWithModuleNameQuery(cmHandleQueryServiceParameters, publicPropertyQueryResult);
-
- return combinedQueryResult == NO_QUERY_EXECUTED
- ? Collections.emptySet() : new HashSet<>(combinedQueryResult.values());
+ return new HashSet<>(combineWithModuleNameQuery(cmHandleQueryServiceParameters, combinedQueryResult).values());
}
/**
@@ -92,52 +94,24 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
return getAllCmHandleIds();
}
- final Map<String, NcmpServiceCmHandle> publicPropertyQueryResult
- = executePublicPropertyQueries(cmHandleQueryServiceParameters);
+ final Map<String, NcmpServiceCmHandle> combinedQueryResult = executeInventoryQueries(
+ cmHandleQueryServiceParameters);
final Collection<String> moduleNamesForQuery =
getModuleNamesForQuery(cmHandleQueryServiceParameters.getCmHandleQueryParameters());
if (moduleNamesForQuery.isEmpty()) {
- return publicPropertyQueryResult == NO_QUERY_EXECUTED
- ? Collections.emptySet() : publicPropertyQueryResult.keySet();
+ return combinedQueryResult.keySet();
}
final Set<String> moduleNameQueryResult = getNamesOfAnchorsWithGivenModules(moduleNamesForQuery);
- if (publicPropertyQueryResult == NO_QUERY_EXECUTED) {
+ if (combinedQueryResult == NO_QUERY_TO_EXECUTE) {
return moduleNameQueryResult;
}
- moduleNameQueryResult.retainAll(publicPropertyQueryResult.keySet());
+ moduleNameQueryResult.retainAll(combinedQueryResult.keySet());
return moduleNameQueryResult;
}
- private Map<String, NcmpServiceCmHandle> executePublicPropertyQueries(
- final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) {
- final Map<String, String> publicPropertyQueryPairs =
- getPublicPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters());
- if (publicPropertyQueryPairs.isEmpty()) {
- return NO_QUERY_EXECUTED;
- }
- Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandles = null;
- for (final Map.Entry<String, String> entry : publicPropertyQueryPairs.entrySet()) {
- final String cpsPath = "//public-properties[@name='" + entry.getKey() + "' and @value='"
- + entry.getValue() + "']/ancestor::cm-handles";
-
- final Collection<DataNode> dataNodes = inventoryPersistence.queryDataNodes(cpsPath);
- if (cmHandleIdToNcmpServiceCmHandles == NO_QUERY_EXECUTED) {
- cmHandleIdToNcmpServiceCmHandles = collectDataNodesToNcmpServiceCmHandles(dataNodes);
- } else {
- final Collection<String> cmHandleIdsToRetain = dataNodes.parallelStream()
- .map(dataNode -> dataNode.getLeaves().get("id").toString()).collect(Collectors.toSet());
- cmHandleIdToNcmpServiceCmHandles.keySet().retainAll(cmHandleIdsToRetain);
- }
- if (cmHandleIdToNcmpServiceCmHandles.isEmpty()) {
- break;
- }
- }
- return cmHandleIdToNcmpServiceCmHandles;
- }
-
private Map<String, NcmpServiceCmHandle> combineWithModuleNameQuery(
final CmHandleQueryServiceParameters cmHandleQueryServiceParameters,
final Map<String, NcmpServiceCmHandle> previousQueryResult) {
@@ -151,7 +125,7 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
return Collections.emptyMap();
}
final Map<String, NcmpServiceCmHandle> queryResult = new HashMap<>(cmHandleIdsByModuleName.size());
- if (previousQueryResult == NO_QUERY_EXECUTED) {
+ if (previousQueryResult == NO_QUERY_TO_EXECUTE) {
cmHandleIdsByModuleName.forEach(cmHandleId ->
queryResult.put(cmHandleId, createNcmpServiceCmHandle(
inventoryPersistence.getDataNode("/dmi-registry/cm-handles[@id='" + cmHandleId + "']")))
@@ -163,19 +137,68 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
return queryResult;
}
+ private Map<String, NcmpServiceCmHandle> executeInventoryQueries(
+ final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) {
+ final Map<String, String> cpsPath = getCpsPath(cmHandleQueryServiceParameters.getCmHandleQueryParameters());
+ if (!validateCpsPathConditionProperties(cpsPath)) {
+ return Collections.emptyMap();
+ }
+ final Map<String, NcmpServiceCmHandle> cpsPathQueryResult;
+ if (cpsPath.isEmpty()) {
+ cpsPathQueryResult = NO_QUERY_TO_EXECUTE;
+ } else {
+ try {
+ cpsPathQueryResult = cmHandleQueries.getCmHandleDataNodesByCpsPath(
+ cpsPath.get("cpsPath"), INCLUDE_ALL_DESCENDANTS)
+ .stream().map(this::createNcmpServiceCmHandle)
+ .collect(Collectors.toMap(NcmpServiceCmHandle::getCmHandleId,
+ Function.identity()));
+ } catch (final PathParsingException pathParsingException) {
+ throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(),
+ pathParsingException);
+ }
+ if (cpsPathQueryResult.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ }
+
+ final Map<String, String> publicPropertyQueryPairs =
+ getPublicPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters());
+ final Map<String, NcmpServiceCmHandle> propertiesQueryResult = publicPropertyQueryPairs.isEmpty()
+ ? NO_QUERY_TO_EXECUTE : cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs);
+
+ return cmHandleQueries.combineCmHandleQueries(cpsPathQueryResult, propertiesQueryResult);
+ }
+
private Set<String> getNamesOfAnchorsWithGivenModules(final Collection<String> moduleNamesForQuery) {
final Collection<Anchor> anchors = inventoryPersistence.queryAnchors(moduleNamesForQuery);
return anchors.parallelStream().map(Anchor::getName).collect(Collectors.toSet());
}
- private Map<String, NcmpServiceCmHandle> collectDataNodesToNcmpServiceCmHandles(
- final Collection<DataNode> dataNodes) {
- final Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandle = new HashMap<>();
- dataNodes.forEach(dataNode -> {
- final NcmpServiceCmHandle ncmpServiceCmHandle = createNcmpServiceCmHandle(dataNode);
- cmHandleIdToNcmpServiceCmHandle.put(ncmpServiceCmHandle.getCmHandleId(), ncmpServiceCmHandle);
- });
- return cmHandleIdToNcmpServiceCmHandle;
+ private Collection<String> getModuleNamesForQuery(final List<ConditionProperties> conditionProperties) {
+ final List<String> result = new ArrayList<>();
+ getConditions(conditionProperties, ValidQueryProperties.HAS_ALL_MODULES.getQueryProperty())
+ .parallelStream().forEach(
+ conditionProperty -> {
+ validateModuleNameConditionProperties(conditionProperty);
+ result.add(conditionProperty.get("moduleName"));
+ }
+ );
+ return result;
+ }
+
+ private Map<String, String> getCpsPath(final List<ConditionProperties> conditionProperties) {
+ final Map<String, String> result = new HashMap<>();
+ getConditions(conditionProperties, ValidQueryProperties.WITH_CPS_PATH.getQueryProperty()).forEach(
+ result::putAll);
+ return result;
+ }
+
+ private Map<String, String> getPublicPropertyPairs(final List<ConditionProperties> conditionProperties) {
+ final Map<String, String> result = new HashMap<>();
+ getConditions(conditionProperties,
+ ValidQueryProperties.HAS_ALL_PROPERTIES.getQueryProperty()).forEach(result::putAll);
+ return result;
}
private List<Map<String, String>> getConditions(final List<ConditionProperties> conditionProperties,
@@ -188,23 +211,6 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
return Collections.emptyList();
}
- private Collection<String> getModuleNamesForQuery(final List<ConditionProperties> conditionProperties) {
- final List<String> result = new ArrayList<>();
- getConditions(conditionProperties, MODULE_QUERY_NAME).parallelStream().forEach(
- conditionProperty -> {
- validateModuleNameConditionProperties(conditionProperty);
- result.add(conditionProperty.get("moduleName"));
- }
- );
- return result;
- }
-
- private Map<String, String> getPublicPropertyPairs(final List<ConditionProperties> conditionProperties) {
- final Map<String, String> result = new HashMap<>();
- getConditions(conditionProperties, PROPERTY_QUERY_NAME).forEach(result::putAll);
- return result;
- }
-
private Set<NcmpServiceCmHandle> getAllCmHandles() {
return inventoryPersistence.getDataNode("/dmi-registry")
.getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet());
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
index 8d32c1ade5..28cbf2cc24 100755
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
@@ -271,7 +271,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
lcmEventsCmHandleStateHandler.updateCmHandleState(yangModelCmHandle,
CmHandleState.DELETING);
- deleteSchemaSetAndListElementByCmHandleId(cmHandleId);
+ deleteCmHandleByCmHandleId(cmHandleId);
cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId));
lcmEventsCmHandleStateHandler.updateCmHandleState(yangModelCmHandle,
CmHandleState.DELETED);
@@ -295,7 +295,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
return cmHandleRegistrationResponses;
}
- private void deleteSchemaSetAndListElementByCmHandleId(final String cmHandleId) {
+ private void deleteCmHandleByCmHandleId(final String cmHandleId) {
inventoryPersistence.deleteSchemaSetWithCascade(cmHandleId);
inventoryPersistence.deleteListOrListElement("/dmi-registry/cm-handles[@id='" + cmHandleId + "']");
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueries.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueries.java
new file mode 100644
index 0000000000..92387bab32
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueries.java
@@ -0,0 +1,165 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * 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.ncmp.api.inventory;
+
+import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle;
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.spi.CpsDataPersistenceService;
+import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.model.DataNode;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class CmHandleQueries {
+
+ private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
+ private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
+
+ private final CpsDataPersistenceService cpsDataPersistenceService;
+ private static final Map<String, NcmpServiceCmHandle> NO_QUERY_TO_EXECUTE = null;
+ private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles";
+
+
+ /**
+ * Query CmHandles based on PublicProperties.
+ *
+ * @param publicPropertyQueryPairs public properties for query
+ * @return CmHandles which have these public properties
+ */
+ public Map<String, NcmpServiceCmHandle> queryCmHandlePublicProperties(
+ final Map<String, String> publicPropertyQueryPairs) {
+ if (publicPropertyQueryPairs.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandles = null;
+ for (final Map.Entry<String, String> publicPropertyQueryPair : publicPropertyQueryPairs.entrySet()) {
+ final String cpsPath = "//public-properties[@name=\"" + publicPropertyQueryPair.getKey()
+ + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]";
+
+ final Collection<DataNode> dataNodes = getCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS);
+ if (cmHandleIdToNcmpServiceCmHandles == null) {
+ cmHandleIdToNcmpServiceCmHandles = collectDataNodesToNcmpServiceCmHandles(dataNodes);
+ } else {
+ final Collection<String> cmHandleIdsToRetain = dataNodes.parallelStream()
+ .map(dataNode -> dataNode.getLeaves().get("id").toString()).collect(Collectors.toSet());
+ cmHandleIdToNcmpServiceCmHandles.keySet().retainAll(cmHandleIdsToRetain);
+ }
+ if (cmHandleIdToNcmpServiceCmHandles.isEmpty()) {
+ break;
+ }
+ }
+ return cmHandleIdToNcmpServiceCmHandles;
+ }
+
+ /**
+ * Combine Maps of CmHandles.
+ *
+ * @param firstQuery first CmHandles Map
+ * @param secondQuery second CmHandles Map
+ * @return combined Map of CmHandles
+ */
+ public Map<String, NcmpServiceCmHandle> combineCmHandleQueries(
+ final Map<String, NcmpServiceCmHandle> firstQuery,
+ final Map<String, NcmpServiceCmHandle> secondQuery) {
+ if (firstQuery == NO_QUERY_TO_EXECUTE && secondQuery == NO_QUERY_TO_EXECUTE) {
+ return Collections.emptyMap();
+ } else if (firstQuery == NO_QUERY_TO_EXECUTE) {
+ return secondQuery;
+ } else if (secondQuery == NO_QUERY_TO_EXECUTE) {
+ return firstQuery;
+ } else {
+ firstQuery.keySet().retainAll(secondQuery.keySet());
+ return firstQuery;
+ }
+ }
+
+ /**
+ * Method which returns cm handles by the cm handles state.
+ *
+ * @param cmHandleState cm handle state
+ * @return a list of cm handles
+ */
+ public List<DataNode> getCmHandlesByState(final CmHandleState cmHandleState) {
+ return getCmHandleDataNodesByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]",
+ INCLUDE_ALL_DESCENDANTS);
+ }
+
+ /**
+ * Method to return data nodes representing the cm handles.
+ *
+ * @param cpsPath cps path for which the cmHandle is requested
+ * @return a list of data nodes representing the cm handles.
+ */
+ public List<DataNode> getCmHandleDataNodesByCpsPath(final String cpsPath,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption);
+ }
+
+ /**
+ * Method which returns cm handles by the cm handle id and state.
+ * @param cmHandleId cm handle id
+ * @param cmHandleState cm handle state
+ * @return a list of cm handles
+ */
+ public List<DataNode> getCmHandlesByIdAndState(final String cmHandleId, final CmHandleState cmHandleState) {
+ return getCmHandleDataNodesByCpsPath("//cm-handles[@id='" + cmHandleId + "']/state[@cm-handle-state=\""
+ + cmHandleState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS);
+ }
+
+ /**
+ * Method which returns cm handles by the operational sync state of cm handle.
+ * @param dataStoreSyncState sync state
+ * @return a list of cm handles
+ */
+ public List<DataNode> getCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) {
+ return getCmHandleDataNodesByCpsPath("//state/datastores" + "/operational[@sync-state=\""
+ + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS);
+ }
+
+ private Map<String, NcmpServiceCmHandle> collectDataNodesToNcmpServiceCmHandles(
+ final Collection<DataNode> dataNodes) {
+ final Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandle = new HashMap<>();
+ dataNodes.forEach(dataNode -> {
+ final NcmpServiceCmHandle ncmpServiceCmHandle = createNcmpServiceCmHandle(dataNode);
+ cmHandleIdToNcmpServiceCmHandle.put(ncmpServiceCmHandle.getCmHandleId(), ncmpServiceCmHandle);
+ });
+ return cmHandleIdToNcmpServiceCmHandle;
+ }
+
+ private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) {
+ return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter
+ .convertCmHandleToYangModel(dataNode, dataNode.getLeaves().get("id").toString()));
+ }
+}
+
+
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
index be26a58d58..14fc6d698a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
@@ -28,7 +28,6 @@ import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
import java.time.OffsetDateTime;
import java.util.Collection;
-import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.CpsDataService;
@@ -60,8 +59,6 @@ public class InventoryPersistence {
private static final String CM_HANDLE_XPATH_TEMPLATE = "/dmi-registry/cm-handles[@id='" + "%s" + "']";
- private static final String ANCESTOR_CM_HANDLES = "\"]/ancestor::cm-handles";
-
private final JsonObjectMapper jsonObjectMapper;
private final CpsDataService cpsDataService;
@@ -100,57 +97,6 @@ public class InventoryPersistence {
}
/**
- * Method which returns cm handles by the cm handles state.
- *
- * @param cmHandleState cm handle state
- * @return a list of cm handles
- */
- public List<DataNode> getCmHandlesByState(final CmHandleState cmHandleState) {
- return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME,
- NCMP_DMI_REGISTRY_ANCHOR, "//state[@cm-handle-state=\""
- + cmHandleState + ANCESTOR_CM_HANDLES,
- FetchDescendantsOption.OMIT_DESCENDANTS);
- }
-
- /**
- * Method to return data nodes representing the cm handles.
- *
- * @param cpsPath cps path for which the cmHandle is requested
- * @param fetchDescendantsOption defines the scope of data to fetch: either single node or all the descendant nodes
- * @return a list of data nodes representing the cm handles.
- */
- public List<DataNode> getCmHandleDataNodesByCpsPath(final String cpsPath,
- final FetchDescendantsOption fetchDescendantsOption) {
- return cpsDataPersistenceService.queryDataNodes(
- NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsPath, fetchDescendantsOption);
- }
-
- /**
- * Method which returns cm handles by the cm handle id and state.
- * @param cmHandleId cm handle id
- * @param cmHandleState cm handle state
- * @return a list of cm handles
- */
- public List<DataNode> getCmHandlesByIdAndState(final String cmHandleId, final CmHandleState cmHandleState) {
- return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME,
- NCMP_DMI_REGISTRY_ANCHOR, "//cm-handles[@id='" + cmHandleId + "']/state[@cm-handle-state=\""
- + cmHandleState + ANCESTOR_CM_HANDLES,
- FetchDescendantsOption.OMIT_DESCENDANTS);
- }
-
- /**
- * Method which returns cm handles by the operational sync state of cm handle.
- * @param dataStoreSyncState sync state
- * @return a list of cm handles
- */
- public List<DataNode> getCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) {
- return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME,
- NCMP_DMI_REGISTRY_ANCHOR, "//state/datastores"
- + "/operational[@sync-state=\"" + dataStoreSyncState + ANCESTOR_CM_HANDLES,
- FetchDescendantsOption.OMIT_DESCENDANTS);
- }
-
- /**
* This method retrieves DMI service name, DMI properties and the state for a given cm handle.
* @param cmHandleId the id of the cm handle
* @return yang model cm handle
@@ -219,17 +165,6 @@ public class InventoryPersistence {
}
/**
- * Query data nodes via cps path.
- *
- * @param cpsPath cps path
- * @return List of data nodes
- */
- public List<DataNode> queryDataNodes(final String cpsPath) {
- return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- cpsPath, INCLUDE_ALL_DESCENDANTS);
- }
-
- /**
* Get data node via xpath.
*
* @param xpath xpath
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
index 467fd8f608..2b7d3c99cb 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
@@ -40,6 +40,7 @@ import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.CmHandleQueries;
import org.onap.cps.ncmp.api.inventory.CmHandleState;
import org.onap.cps.ncmp.api.inventory.CompositeState;
import org.onap.cps.ncmp.api.inventory.DataStoreSyncState;
@@ -57,6 +58,8 @@ import org.springframework.stereotype.Service;
public class SyncUtils {
private final InventoryPersistence inventoryPersistence;
+ private final CmHandleQueries cmHandleQueries;
+
private final DmiDataOperations dmiDataOperations;
private final JsonObjectMapper jsonObjectMapper;
@@ -70,7 +73,7 @@ public class SyncUtils {
*/
public List<YangModelCmHandle> getAdvisedCmHandles() {
final List<DataNode> advisedCmHandlesAsDataNodeList = new ArrayList<>(
- inventoryPersistence.getCmHandlesByState(CmHandleState.ADVISED));
+ cmHandleQueries.getCmHandlesByState(CmHandleState.ADVISED));
log.info("Total number of fetched advised cm handle(s) is (are) {}", advisedCmHandlesAsDataNodeList.size());
if (advisedCmHandlesAsDataNodeList.isEmpty()) {
return Collections.emptyList();
@@ -87,16 +90,16 @@ public class SyncUtils {
* return null if not found
*/
public YangModelCmHandle getAnUnSynchronizedReadyCmHandle() {
- final List<DataNode> unSynchronizedCmHandles = inventoryPersistence
- .getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED);
+ final List<DataNode> unSynchronizedCmHandles = cmHandleQueries
+ .getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED);
if (unSynchronizedCmHandles.isEmpty()) {
return null;
}
Collections.shuffle(unSynchronizedCmHandles);
for (final DataNode cmHandle : unSynchronizedCmHandles) {
final String cmHandleId = cmHandle.getLeaves().get("id").toString();
- final List<DataNode> readyCmHandles = inventoryPersistence
- .getCmHandlesByIdAndState(cmHandleId, CmHandleState.READY);
+ final List<DataNode> readyCmHandles = cmHandleQueries
+ .getCmHandlesByIdAndState(cmHandleId, CmHandleState.READY);
if (!readyCmHandles.isEmpty()) {
return inventoryPersistence.getYangModelCmHandle(cmHandleId);
}
@@ -110,8 +113,8 @@ public class SyncUtils {
* @return a random LOCKED yang model cm handle, return null if not found
*/
public List<YangModelCmHandle> getModuleSyncFailedCmHandles() {
- final List<DataNode> lockedCmHandlesAsDataNodeList = inventoryPersistence.getCmHandleDataNodesByCpsPath(
- "//lock-reason[@reason=\"LOCKED_MODULE_SYNC_FAILED\"]/ancestor::cm-handles",
+ final List<DataNode> lockedCmHandlesAsDataNodeList = cmHandleQueries.getCmHandleDataNodesByCpsPath(
+ "//lock-reason[@reason=\"LOCKED_MODULE_SYNC_FAILED\"]",
FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList);
}
@@ -171,8 +174,8 @@ public class SyncUtils {
*/
public String getResourceData(final String cmHandleId) {
final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi(
- cmHandleId, DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
- UUID.randomUUID().toString());
+ cmHandleId, DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
+ UUID.randomUUID().toString());
if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) {
return getFirstResource(resourceDataResponseEntity.getBody());
}
@@ -188,8 +191,8 @@ public class SyncUtils {
}
private List<YangModelCmHandle> convertCmHandlesDataNodesToYangModelCmHandles(
- final List<DataNode> cmHandlesAsDataNodeList) {
+ final List<DataNode> cmHandlesAsDataNodeList) {
return cmHandlesAsDataNodeList.stream().map(dataNode -> YangDataConverter.convertCmHandleToYangModel(dataNode,
- dataNode.getLeaves().get("id").toString())).collect(Collectors.toList());
+ dataNode.getLeaves().get("id").toString())).collect(Collectors.toList());
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
index 7cf572ddb1..40ec12da87 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
@@ -20,8 +20,14 @@
package org.onap.cps.ncmp.api.impl
+import org.onap.cps.cpspath.parser.PathParsingException
import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
import org.onap.cps.ncmp.api.inventory.InventoryPersistence
+import org.onap.cps.ncmp.api.inventory.CmHandleQueries
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.spi.exceptions.DataInUseException
+import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.Anchor
import org.onap.cps.spi.model.CmHandleQueryServiceParameters
import org.onap.cps.spi.model.ConditionProperties
@@ -32,87 +38,127 @@ import java.util.stream.Collectors
class NetworkCmProxyCmHandlerQueryServiceSpec extends Specification {
+ def cmHandleQueries = Mock(CmHandleQueries)
def inventoryPersistence = Mock(InventoryPersistence)
- NetworkCmProxyCmHandlerQueryService objectUnderTest = new NetworkCmProxyCmHandlerQueryServiceImpl(inventoryPersistence)
+ def static someCmHandleDataNode = new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'some-cmhandle-id\']', leaves: ['id':'some-cmhandle-id'])
+ def dmiRegistry = new DataNode(xpath: '/dmi-registry', childDataNodes: createDataNodeList(['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4']))
- def 'Retrieve cm handles with public properties when #scenario.'() {
- given: 'a condition property'
+ def objectUnderTest = new NetworkCmProxyCmHandlerQueryServiceImpl(cmHandleQueries, inventoryPersistence)
+
+ def 'Retrieve cm handles with cpsPath when combined with no Module Query.'() {
+ given: 'a cmHandleWithCpsPath condition property'
def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
- def conditionProperties = new ConditionProperties()
- conditionProperties.conditionName = 'hasAllProperties'
- conditionProperties.conditionParameters = publicProperties
+ def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']])
cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
- and: 'mock services'
- mockResponses()
- when: 'a query is execute (with and without Data)'
+ and: 'cmHandleQueries returns a non null query result'
+ cmHandleQueries.getCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])]
+ and: 'CmHandleQueries returns cmHandles with the relevant query result'
+ cmHandleQueries.combineCmHandleQueries(*_) >> ['PNFDemo1': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo1'), 'PNFDemo3': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo3')]
+ when: 'the query is executed for both cm handle ids and details'
def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
then: 'the correct expected cm handles ids are returned'
- returnedCmHandlesJustIds == expectedCmHandleIds as Set
- and: 'the correct cm handle data objects are returned'
- returnedCmHandlesWithData.stream().map(dataNode -> dataNode.cmHandleId).collect(Collectors.toSet()) == expectedCmHandleIds as Set
+ returnedCmHandlesJustIds == ['PNFDemo1', 'PNFDemo3'] as Set
+ and: 'the correct ncmp service cm handles are returned'
+ returnedCmHandlesWithData.stream().map(CmHandle -> CmHandle.cmHandleId).collect(Collectors.toSet()) == ['PNFDemo1', 'PNFDemo3'] as Set
+ }
+
+ def 'Retrieve cm handles with cpsPath where #scenario.'() {
+ given: 'a cmHandleWithCpsPath condition property'
+ def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
+ def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']])
+ cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
+ and: 'cmHandleQueries throws a path parsing exception'
+ cmHandleQueries.getCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> { throw thrownException }
+ when: 'the query is executed for both cm handle ids and details'
+ objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
+ objectUnderTest.queryCmHandles(cmHandleQueryParameters)
+ then: 'a data validation exception is thrown'
+ thrown(expectedException)
where: 'the following data is used'
- scenario | publicProperties || expectedCmHandleIds
- 'single property matches' | [['Contact' : 'newemailforstore@bookstore.com']] || ['PNFDemo1', 'PNFDemo2', 'PNFDemo4']
- 'public property does not match' | [['wont_match' : 'wont_match']] || []
- '2 properties, only one match' | [['Contact' : 'newemailforstore@bookstore.com'], ['Contact2': 'newemailforstore2@bookstore.com']] || ['PNFDemo4']
- '2 properties, no matches' | [['Contact' : 'newemailforstore@bookstore.com'], ['Contact2': '']] || []
+ scenario | thrownException || expectedException
+ 'a PathParsingException is thrown' | new PathParsingException('some message', 'some details') || DataValidationException
+ 'any other Exception is thrown' | new DataInUseException('some message', 'some details') || DataInUseException
}
- def 'Retrieve cm handles with module names when #scenario.'() {
- given: 'a condition property'
+ def 'Query cm handles with public properties when combined with empty modules query result.'() {
+ given: 'a public properties condition property'
def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
- def conditionProperties = new ConditionProperties()
- conditionProperties.conditionName = 'hasAllModules'
- conditionProperties.conditionParameters = moduleNames
+ def conditionProperties = createConditionProperties('hasAllProperties', [['some-property-key': 'some-property-value']])
cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
- and: 'mock services'
- mockResponses()
- when: 'the service is invoked'
+ and: 'CmHandleQueries returns cmHandles with the relevant query result'
+ cmHandleQueries.combineCmHandleQueries(*_) >> ['PNFDemo1': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo1'), 'PNFDemo3': new NcmpServiceCmHandle(cmHandleId: 'PNFDemo3')]
+ when: 'the query is executed for both cm handle ids and details'
def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
- then: 'the correct expected cm handles are returned'
+ then: 'the correct expected cm handles ids are returned'
+ returnedCmHandlesJustIds == ['PNFDemo1', 'PNFDemo3'] as Set
+ and: 'the correct cm handle data objects are returned'
+ returnedCmHandlesWithData.stream().map(dataNode -> dataNode.cmHandleId).collect(Collectors.toSet()) == ['PNFDemo1', 'PNFDemo3'] as Set
+ }
+
+ def 'Retrieve cm handles with module names when #scenario from query.'() {
+ given: 'a modules condition property'
+ def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
+ def conditionProperties = createConditionProperties('hasAllModules', [['moduleName': 'some-module-name']])
+ cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
+ and: 'null is returned from the state and public property queries'
+ cmHandleQueries.combineCmHandleQueries(*_) >> null
+ and: '#scenario from the modules query'
+ inventoryPersistence.queryAnchors(*_) >> returnedAnchors
+ and: 'the same cmHandles are returned from the persistence service layer'
+ returnedAnchors.size() * inventoryPersistence.getDataNode(*_) >> returnedCmHandles
+ when: 'the query is executed for both cm handle ids and details'
+ def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
+ def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
+ then: 'the correct expected cm handles ids are returned'
returnedCmHandlesJustIds == expectedCmHandleIds as Set
+ and: 'the correct cm handle data objects are returned'
returnedCmHandlesWithData.stream().map(dataNode -> dataNode.cmHandleId).collect(Collectors.toSet()) == expectedCmHandleIds as Set
where: 'the following data is used'
- scenario | moduleNames || expectedCmHandleIds
- 'single matching module name' | [['moduleName' : 'MODULE-NAME-001']] || ['PNFDemo3', 'PNFDemo1', 'PNFDemo2']
- 'module name dont match' | [['moduleName' : 'MODULE-NAME-004']] || []
- '2 module names, only one match' | [['moduleName' : 'MODULE-NAME-002'], ['moduleName': 'MODULE-NAME-003']] || ['PNFDemo4']
- '2 module names, no matches' | [['moduleName' : 'MODULE-NAME-002'], ['moduleName': 'MODULE-NAME-004']] || []
+ scenario | returnedAnchors | returnedCmHandles || expectedCmHandleIds
+ 'One anchor returned' | [new Anchor(name: 'some-cmhandle-id')] | someCmHandleDataNode || ['some-cmhandle-id']
+ 'No anchors are returned' | [] | null || []
}
def 'Retrieve cm handles with combined queries when #scenario.'() {
- given: 'condition properties'
+ given: 'all condition properties used'
def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
- def conditionProperties1 = new ConditionProperties()
- conditionProperties1.conditionName = 'hasAllProperties'
- conditionProperties1.conditionParameters = publicProperties
- def conditionProperties2 = new ConditionProperties()
- conditionProperties2.conditionName = 'hasAllModules'
- conditionProperties2.conditionParameters = moduleNames
- cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties1,conditionProperties2])
- and: 'mock services'
- mockResponses()
- when: 'the service is invoked'
+ def conditionPubProps = createConditionProperties('hasAllProperties', [['some-property-key': 'some-property-value']])
+ def conditionModules = createConditionProperties('hasAllModules', [['moduleName': 'some-module-name']])
+ def conditionState = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']])
+ cmHandleQueryParameters.setCmHandleQueryParameters([conditionPubProps, conditionModules, conditionState])
+ and: 'cmHandles are returned from the state and public property combined queries'
+ cmHandleQueries.combineCmHandleQueries(*_) >> combinedQueryMap
+ and: 'cmHandles are returned from the module names query'
+ inventoryPersistence.queryAnchors(['some-module-name']) >> anchorsForModuleQuery
+ and: 'cmHandleQueries returns a datanode result'
+ 2 * cmHandleQueries.getCmHandleDataNodesByCpsPath(*_) >> [someCmHandleDataNode]
+ when: 'the query is executed for both cm handle ids and details'
def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
- then: 'the correct expected cm handles are returned'
+ then: 'the correct expected cm handles ids are returned'
returnedCmHandlesJustIds == expectedCmHandleIds as Set
- returnedCmHandlesWithData.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == expectedCmHandleIds as Set
+ and: 'the correct cm handle data objects are returned'
+ returnedCmHandlesWithData.stream().map(dataNode -> dataNode.cmHandleId).collect(Collectors.toSet()) == expectedCmHandleIds as Set
where: 'the following data is used'
- scenario | moduleNames | publicProperties || expectedCmHandleIds
- 'particularly intersect' | [['moduleName' : 'MODULE-NAME-001']] | [['Contact' : 'newemailforstore@bookstore.com']] || ['PNFDemo1', 'PNFDemo2']
- 'empty intersect' | [['moduleName' : 'MODULE-NAME-004']] | [['Contact' : 'newemailforstore@bookstore.com']] || []
- 'total intersect' | [['moduleName' : 'MODULE-NAME-002']] | [['Contact2' : 'newemailforstore2@bookstore.com']] || ['PNFDemo4']
+ scenario | combinedQueryMap | anchorsForModuleQuery || expectedCmHandleIds
+ 'combined and modules queries intersect' | ['PNFDemo1' : new NcmpServiceCmHandle(cmHandleId:'PNFDemo1')] | [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2')] || ['PNFDemo1']
+ 'only module query results exist' | [:] | [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2')] || []
+ 'only combined query results exist' | ['PNFDemo1' : new NcmpServiceCmHandle(cmHandleId:'PNFDemo1'), 'PNFDemo2' : new NcmpServiceCmHandle(cmHandleId:'PNFDemo2')] | [] || []
+ 'neither queries return results' | [:] | [] || []
+ 'none intersect' | ['PNFDemo1' : new NcmpServiceCmHandle(cmHandleId:'PNFDemo1')] | [new Anchor(name: 'PNFDemo2')] || []
}
def 'Retrieve cm handles when the query is empty.'() {
- given: 'mock services'
- mockResponses()
- when: 'the service is invoked'
+ given: 'We use an empty query'
def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
+ and: 'the inventory persistence returns the dmi registry datanode'
+ inventoryPersistence.getDataNode("/dmi-registry") >> dmiRegistry
+ and: 'the inventory persistence returns anchors for get anchors'
+ inventoryPersistence.getAnchors() >> [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo3'), new Anchor(name: 'PNFDemo4')]
+ when: 'the query is executed for both cm handle ids and details'
def returnedCmHandlesJustIds = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
def returnedCmHandlesWithData = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
then: 'the correct expected cm handles are returned'
@@ -120,43 +166,13 @@ class NetworkCmProxyCmHandlerQueryServiceSpec extends Specification {
returnedCmHandlesWithData.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4'] as Set
}
- void mockResponses() {
- def pNFDemo1 = new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'PNFDemo1\']', leaves: ['id':'PNFDemo1'])
- def pNFDemo2 = new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'PNFDemo2\']', leaves: ['id':'PNFDemo2'])
- def pNFDemo3 = new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'PNFDemo3\']', leaves: ['id':'PNFDemo3'])
- def pNFDemo4 = new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'PNFDemo4\']', leaves: ['id':'PNFDemo4'])
- def dmiRegistry = new DataNode(xpath: '/dmi-registry', childDataNodes: [pNFDemo1, pNFDemo2, pNFDemo3, pNFDemo4])
-
- inventoryPersistence.queryDataNodes('//public-properties[@name=\'Contact\' and @value=\'newemailforstore@bookstore.com\']/ancestor::cm-handles')
- >> [pNFDemo1, pNFDemo2, pNFDemo4]
- inventoryPersistence.queryDataNodes('//public-properties[@name=\'wont_match\' and @value=\'wont_match\']/ancestor::cm-handles')
- >> []
- inventoryPersistence.queryDataNodes('//public-properties[@name=\'Contact2\' and @value=\'newemailforstore2@bookstore.com\']/ancestor::cm-handles')
- >> [pNFDemo4]
- inventoryPersistence.queryDataNodes('//public-properties[@name=\'Contact2\' and @value=\'\']/ancestor::cm-handles')
- >> []
- inventoryPersistence.queryDataNodes('//public-properties/ancestor::cm-handles')
- >> [pNFDemo1, pNFDemo2, pNFDemo3, pNFDemo4]
-
- inventoryPersistence.queryDataNodes('//cm-handles[@id=\'PNFDemo\']') >> [pNFDemo1]
- inventoryPersistence.queryDataNodes('//cm-handles[@id=\'PNFDemo2\']') >> [pNFDemo2]
- inventoryPersistence.queryDataNodes('//cm-handles[@id=\'PNFDemo3\']') >> [pNFDemo3]
- inventoryPersistence.queryDataNodes('//cm-handles[@id=\'PNFDemo4\']') >> [pNFDemo4]
-
- inventoryPersistence.getDataNode('/dmi-registry') >> dmiRegistry
-
- inventoryPersistence.getDataNode('/dmi-registry/cm-handles[@id=\'PNFDemo1\']') >> pNFDemo1
- inventoryPersistence.getDataNode('/dmi-registry/cm-handles[@id=\'PNFDemo2\']') >> pNFDemo2
- inventoryPersistence.getDataNode('/dmi-registry/cm-handles[@id=\'PNFDemo3\']') >> pNFDemo3
- inventoryPersistence.getDataNode('/dmi-registry/cm-handles[@id=\'PNFDemo4\']') >> pNFDemo4
+ def createConditionProperties(String conditionName, List<Map<String, String>> conditionParameters) {
+ return new ConditionProperties(conditionName : conditionName, conditionParameters : conditionParameters)
+ }
- inventoryPersistence.queryAnchors(['MODULE-NAME-001']) >> [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo3')]
- inventoryPersistence.queryAnchors(['MODULE-NAME-004']) >> []
- inventoryPersistence.queryAnchors(['MODULE-NAME-003', 'MODULE-NAME-002']) >> [new Anchor(name: 'PNFDemo4')]
- inventoryPersistence.queryAnchors(['MODULE-NAME-002', 'MODULE-NAME-003']) >> [new Anchor(name: 'PNFDemo4')]
- inventoryPersistence.queryAnchors(['MODULE-NAME-004', 'MODULE-NAME-002']) >> []
- inventoryPersistence.queryAnchors(['MODULE-NAME-002', 'MODULE-NAME-004']) >> []
- inventoryPersistence.queryAnchors(['MODULE-NAME-002']) >> [new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo4')]
- inventoryPersistence.getAnchors() >> [new Anchor(name: 'PNFDemo1'), new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo3'), new Anchor(name: 'PNFDemo4')]
+ def static createDataNodeList(dataNodeIds) {
+ def dataNodes =[]
+ dataNodeIds.forEach(id -> {dataNodes.add(new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'' + id + '\']', leaves: ['id':id]))})
+ return dataNodes
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesSpec.groovy
new file mode 100644
index 0000000000..10a5d62461
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesSpec.groovy
@@ -0,0 +1,150 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * 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.ncmp.api.inventory
+
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+import org.onap.cps.spi.CpsDataPersistenceService
+import org.onap.cps.spi.model.DataNode
+import spock.lang.Shared
+import spock.lang.Specification
+
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+
+class CmHandleQueriesSpec extends Specification {
+ def cpsDataPersistenceService = Mock(CpsDataPersistenceService)
+
+ def objectUnderTest = new CmHandleQueries(cpsDataPersistenceService)
+
+ @Shared
+ def static sampleDataNodes = [new DataNode()]
+
+ def static pnfDemo = createDataNode('PNFDemo')
+ def static pnfDemo2 = createDataNode('PNFDemo2')
+ def static pnfDemo3 = createDataNode('PNFDemo3')
+ def static pnfDemo4 = createDataNode('PNFDemo4')
+
+ def static pnfDemoCmHandle = new NcmpServiceCmHandle(cmHandleId: 'PNFDemo')
+ def static pnfDemo2CmHandle = new NcmpServiceCmHandle(cmHandleId: 'PNFDemo2')
+ def static pnfDemo3CmHandle = new NcmpServiceCmHandle(cmHandleId: 'PNFDemo3')
+
+ def 'Query CmHandles with public properties query pair.'() {
+ given: 'the DataNodes queried for a given cpsPath are returned from the persistence service.'
+ mockResponses()
+ when: 'a query on cmhandle public properties is performed with a public property pair'
+ def returnedCmHandlesWithData = objectUnderTest.queryCmHandlePublicProperties(publicPropertyPairs)
+ then: 'the correct cm handle data objects are returned'
+ returnedCmHandlesWithData.keySet().containsAll(expectedCmHandleIds)
+ returnedCmHandlesWithData.keySet().size() == expectedCmHandleIds.size()
+ where: 'the following data is used'
+ scenario | publicPropertyPairs || expectedCmHandleIds
+ 'single property matches' | ['Contact' : 'newemailforstore@bookstore.com'] || ['PNFDemo', 'PNFDemo2', 'PNFDemo4']
+ 'public property does not match' | ['wont_match' : 'wont_match'] || []
+ '2 properties, only one match' | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': 'newemailforstore2@bookstore.com'] || ['PNFDemo4']
+ '2 properties, no matches' | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': ''] || []
+ }
+
+ def 'Query CmHandles using empty public properties query pair.'() {
+ when: 'a query on CmHandle public properties is executed using an empty map'
+ def returnedCmHandlesWithData = objectUnderTest.queryCmHandlePublicProperties([:])
+ then: 'no cm handles are returned'
+ returnedCmHandlesWithData.keySet().size() == 0
+ }
+
+ def 'Combine two query results where #scenario.'() {
+ when: 'two query results in the form of a map of NcmpServiceCmHandles are combined into a single query result'
+ def result = objectUnderTest.combineCmHandleQueries(firstQuery, secondQuery)
+ then: 'the returned result is the same as the expected result'
+ result == expectedResult
+ where:
+ scenario | firstQuery | secondQuery || expectedResult
+ 'two queries with unique and non unique entries exist' | ['PNFDemo': pnfDemoCmHandle, 'PNFDemo2': pnfDemo2CmHandle] | ['PNFDemo': pnfDemoCmHandle, 'PNFDemo3': pnfDemo3CmHandle] || ['PNFDemo': pnfDemoCmHandle]
+ 'the first query contains entries and second query is empty' | ['PNFDemo': pnfDemoCmHandle, 'PNFDemo2': pnfDemo2CmHandle] | [:] || [:]
+ 'the second query contains entries and first query is empty' | [:] | ['PNFDemo': pnfDemoCmHandle, 'PNFDemo3': pnfDemo3CmHandle] || [:]
+ 'the first query contains entries and second query is null' | ['PNFDemo': pnfDemoCmHandle, 'PNFDemo2': pnfDemo2CmHandle] | null || ['PNFDemo': pnfDemoCmHandle, 'PNFDemo2': pnfDemo2CmHandle]
+ 'the second query contains entries and first query is null' | null | ['PNFDemo': pnfDemoCmHandle, 'PNFDemo3': pnfDemo3CmHandle] || ['PNFDemo': pnfDemoCmHandle, 'PNFDemo3': pnfDemo3CmHandle]
+ 'both queries are empty' | [:] | [:] || [:]
+ 'both queries are null' | null | null || [:]
+ }
+
+ def 'Get Cm Handles By State'() {
+ given: 'a cm handle state to query'
+ def cmHandleState = CmHandleState.ADVISED
+ and: 'the persistence service returns a list of data nodes'
+ cpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+ '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS) >> sampleDataNodes
+ when: 'cm handles are fetched by state'
+ def result = objectUnderTest.getCmHandlesByState(cmHandleState)
+ then: 'the returned result matches the result from the persistence service'
+ assert result == sampleDataNodes
+ }
+
+ def 'Get Cm Handles By State and Cm-Handle Id'() {
+ given: 'a cm handle state to query'
+ def cmHandleState = CmHandleState.READY
+ and: 'cps data service returns a list of data nodes'
+ cpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+ '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+ when: 'cm handles are fetched by state and id'
+ def result = objectUnderTest.getCmHandlesByIdAndState('some-cm-handle', cmHandleState)
+ then: 'the returned result is a list of data nodes returned by cps data service'
+ assert result == sampleDataNodes
+ }
+
+ def 'Get Cm Handles By Operational Sync State : UNSYNCHRONIZED'() {
+ given: 'a cm handle state to query'
+ def cmHandleState = CmHandleState.READY
+ and: 'cps data service returns a list of data nodes'
+ cpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+ '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+ when: 'cm handles are fetched by the UNSYNCHRONIZED operational sync state'
+ def result = objectUnderTest.getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED)
+ then: 'the returned result is a list of data nodes returned by cps data service'
+ assert result == sampleDataNodes
+ }
+
+ def 'Retrieve cm handle by cps path '() {
+ given: 'a cm handle state to query based on the cps path'
+ def cmHandleDataNode = new DataNode(xpath: 'xpath', leaves: ['cm-handle-state': 'LOCKED'])
+ def cpsPath = '//cps-path'
+ and: 'cps data service returns a valid data node'
+ cpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+ cpsPath + '/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS)
+ >> Arrays.asList(cmHandleDataNode)
+ when: 'get cm handles by cps path is invoked'
+ def result = objectUnderTest.getCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS)
+ then: 'the returned result is a list of data nodes returned by cps data service'
+ assert result.contains(cmHandleDataNode)
+ }
+
+ void mockResponses() {
+ cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact\" and @value=\"newemailforstore@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo2, pnfDemo4]
+ cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"wont_match\" and @value=\"wont_match\"]/ancestor::cm-handles', _) >> []
+ cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"newemailforstore2@bookstore.com\"]/ancestor::cm-handles', _) >> [pnfDemo4]
+ cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"\"]/ancestor::cm-handles', _) >> []
+ cpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"READY\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo3]
+ cpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"LOCKED\"]/ancestor::cm-handles', _) >> [pnfDemo2, pnfDemo4]
+ }
+
+ def static createDataNode(dataNodeId) {
+ return new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'' + dataNodeId + '\']', leaves: ['id':dataNodeId])
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
index 7ac231c169..f9ca676f3b 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
@@ -80,9 +80,6 @@ class InventoryPersistenceSpec extends Specification {
@Shared
def childDataNodesForCmHandleWithState = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/state", leaves: ['cm-handle-state': 'ADVISED'])]
- @Shared
- def static sampleDataNodes = [new DataNode()]
-
def "Retrieve CmHandle using datanode with #scenario."() {
given: 'the cps data service returns a data node from the DMI registry'
def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
@@ -157,56 +154,6 @@ class InventoryPersistenceSpec extends Specification {
'DELETING' | CmHandleState.DELETING || '{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
}
- def 'Get Cm Handles By State'() {
- given: 'a cm handle state to query'
- def cmHandleState = CmHandleState.ADVISED
- and: 'cps data service returns a list of data nodes'
- mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
- '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
- when: 'get cm handles by state is invoked'
- def result = objectUnderTest.getCmHandlesByState(cmHandleState)
- then: 'the returned result is a list of data nodes returned by cps data service'
- assert result == sampleDataNodes
- }
-
- def 'Get Cm Handles By State and Cm-Handle Id'() {
- given: 'a cm handle state to query'
- def cmHandleState = CmHandleState.READY
- and: 'cps data service returns a list of data nodes'
- mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
- '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
- when: 'get cm handles by state and id is invoked'
- def result = objectUnderTest.getCmHandlesByIdAndState(cmHandleId, cmHandleState)
- then: 'the returned result is a list of data nodes returned by cps data service'
- assert result == sampleDataNodes
- }
-
- def 'Get Cm Handles By Operational Sync State : UNSYNCHRONIZED'() {
- given: 'a cm handle state to query'
- def cmHandleState = CmHandleState.READY
- and: 'cps data service returns a list of data nodes'
- mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
- '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
- when: 'get cm handles by operational sync state as UNSYNCHRONIZED is invoked'
- def result = objectUnderTest.getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED)
- then: 'the returned result is a list of data nodes returned by cps data service'
- assert result == sampleDataNodes
- }
-
- def 'Retrieve cm handle by cps path '() {
- given: 'a cm handle state to query based on the cps path'
- def cmHandleDataNode = new DataNode(xpath: 'xpath', leaves: ['cm-handle-state': 'LOCKED'])
- def cpsPath = '//cps-path'
- and: 'cps data service returns a valid data node'
- mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
- cpsPath, INCLUDE_ALL_DESCENDANTS)
- >> Arrays.asList(cmHandleDataNode)
- when: 'get cm handles by cps path is invoked'
- def result = objectUnderTest.getCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS)
- then: 'the returned result is a list of data nodes returned by cps data service'
- assert result.contains(cmHandleDataNode)
- }
-
def 'Get module definitions'() {
given: 'cps module service returns a collection of module definitions'
def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
@@ -263,13 +210,6 @@ class InventoryPersistenceSpec extends Specification {
0 * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'sampleSchemaSetName', CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED)
}
- def 'Query data nodes via cpsPath'() {
- when: 'the method to query data nodes is called'
- objectUnderTest.queryDataNodes('sample cpsPath')
- then: 'the data persistence service method to query data nodes is invoked once'
- 1 * mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin','ncmp-dmi-registry','sample cpsPath', INCLUDE_ALL_DESCENDANTS)
- }
-
def 'Get data node via xPath'() {
when: 'the method to get data nodes is called'
objectUnderTest.getDataNode('sample xPath')
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
index 6c2d8f15b3..82e9d33ae7 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
@@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
import org.onap.cps.ncmp.api.impl.operations.DmiOperations
+import org.onap.cps.ncmp.api.inventory.CmHandleQueries
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder
@@ -47,11 +48,13 @@ class SyncUtilsSpec extends Specification{
def mockInventoryPersistence = Mock(InventoryPersistence)
+ def mockCmHandleQueries = Mock(CmHandleQueries)
+
def mockDmiDataOperations = Mock(DmiDataOperations)
def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
- def objectUnderTest = new SyncUtils(mockInventoryPersistence, mockDmiDataOperations, jsonObjectMapper)
+ def objectUnderTest = new SyncUtils(mockInventoryPersistence, mockCmHandleQueries, mockDmiDataOperations, jsonObjectMapper)
@Shared
def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(OffsetDateTime.now())
@@ -61,14 +64,14 @@ class SyncUtilsSpec extends Specification{
def 'Get an advised Cm-Handle where ADVISED cm handle #scenario'() {
given: 'the inventory persistence service returns a collection of data nodes'
- mockInventoryPersistence.getCmHandlesByState(CmHandleState.ADVISED) >> dataNodeCollection
+ mockCmHandleQueries.getCmHandlesByState(CmHandleState.ADVISED) >> dataNodeCollection
when: 'get advised cm handles are fetched'
def yangModelCmHandles = objectUnderTest.getAdvisedCmHandles()
then: 'the returned data node collection is the correct size'
yangModelCmHandles.size() == expectedDataNodeSize
and: 'yang model collection contains the correct data'
yangModelCmHandles.stream().map(yangModel -> yangModel.id).collect(Collectors.toSet()) ==
- dataNodeCollection.stream().map(dataNode -> dataNode.leaves.get("id")).collect(Collectors.toSet())
+ dataNodeCollection.stream().map(dataNode -> dataNode.leaves.get("id")).collect(Collectors.toSet())
where: 'the following scenarios are used'
scenario | dataNodeCollection || expectedCallsToGetYangModelCmHandle | expectedDataNodeSize
'exists' | [dataNode] || 1 | 1
@@ -77,7 +80,7 @@ class SyncUtilsSpec extends Specification{
def 'Update Lock Reason, Details and Attempts where lock reason #scenario'() {
given: 'A locked state'
- def compositeState = new CompositeState(lockReason: lockReason)
+ def compositeState = new CompositeState(lockReason: lockReason)
when: 'update cm handle details and attempts is called'
objectUnderTest.updateLockReasonDetailsAndAttempts(compositeState, LockReasonCategory.LOCKED_MODULE_SYNC_FAILED, 'new error message')
then: 'the composite state lock reason and details are updated'
@@ -91,9 +94,9 @@ class SyncUtilsSpec extends Specification{
def 'Get all locked Cm-Handle where Lock Reason is LOCKED_MODULE_SYNC_FAILED cm handle #scenario'() {
given: 'the cps (persistence service) returns a collection of data nodes'
- mockInventoryPersistence.getCmHandleDataNodesByCpsPath(
- '//lock-reason[@reason="LOCKED_MODULE_SYNC_FAILED"]/ancestor::cm-handles',
- FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode ]
+ mockCmHandleQueries.getCmHandleDataNodesByCpsPath(
+ '//lock-reason[@reason="LOCKED_MODULE_SYNC_FAILED"]',
+ FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode]
when: 'get locked Misbehaving cm handle is called'
def result = objectUnderTest.getModuleSyncFailedCmHandles()
then: 'the returned cm handle collection is the correct size'
@@ -119,8 +122,8 @@ class SyncUtilsSpec extends Specification{
def 'Get a Cm-Handle where Operational Sync state is UnSynchronized and Cm-handle state is READY and #scenario'() {
given: 'the inventory persistence service returns a collection of data nodes'
- mockInventoryPersistence.getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED) >> unSynchronizedDataNodes
- mockInventoryPersistence.getCmHandlesByIdAndState("cm-handle-123", CmHandleState.READY) >> readyDataNodes
+ mockCmHandleQueries.getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED) >> unSynchronizedDataNodes
+ mockCmHandleQueries.getCmHandlesByIdAndState("cm-handle-123", CmHandleState.READY) >> readyDataNodes
when: 'get advised cm handles are fetched'
objectUnderTest.getAnUnSynchronizedReadyCmHandle()
then: 'the returned data node collection is the correct size'
diff --git a/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java b/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java
index c3811eb485..7fe47be2da 100644
--- a/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java
+++ b/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java
@@ -22,18 +22,17 @@ package org.onap.cps.utils;
import com.google.common.base.Strings;
import java.util.Arrays;
-import java.util.List;
import java.util.Map;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.onap.cps.spi.exceptions.DataValidationException;
import org.onap.cps.spi.model.CmHandleQueryServiceParameters;
+@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CmHandleQueryRestParametersValidator {
- private static final List<String> VALID_PROPERTY_NAMES = Arrays.asList("hasAllProperties", "hasAllModules");
-
/**
* Validate cm handle query parameters.
* @param cmHandleQueryServiceParameters name of data to be validated
@@ -45,7 +44,8 @@ public class CmHandleQueryRestParametersValidator {
if (Strings.isNullOrEmpty(conditionApiProperty.getConditionName())) {
throwDataValidationException("Missing 'conditionName' - please supply a valid name.");
}
- if (!VALID_PROPERTY_NAMES.contains(conditionApiProperty.getConditionName())) {
+ if (Arrays.stream(ValidQueryProperties.values()).noneMatch(validQueryProperty ->
+ validQueryProperty.getQueryProperty().equals(conditionApiProperty.getConditionName()))) {
throwDataValidationException(
String.format("Wrong 'conditionName': %s - please supply a valid name.",
conditionApiProperty.getConditionName()));
@@ -89,6 +89,34 @@ public class CmHandleQueryRestParametersValidator {
throwDataValidationException("Wrong module condition property. - please supply a valid condition property.");
}
+ /**
+ * Validate CPS path condition properties.
+ * @param conditionProperty name of data to be validated
+ */
+ public static boolean validateCpsPathConditionProperties(final Map<String, String> conditionProperty) {
+ if (conditionProperty.isEmpty()) {
+ return true;
+ }
+ if (conditionProperty.size() > 1) {
+ throwDataValidationException("Only one condition property is allowed for the CPS path query.");
+ }
+ if (!conditionProperty.containsKey("cpsPath")) {
+ throwDataValidationException(
+ "Wrong CPS path condition property. - expecting \"cpsPath\" as the condition property.");
+ }
+ final String cpsPath = conditionProperty.get("cpsPath");
+ if (cpsPath.isBlank()) {
+ throwDataValidationException(
+ "Wrong CPS path. - please supply a valid CPS path.");
+ }
+ if (cpsPath.contains("/additional-properties")) {
+ log.debug("{} - Private metadata cannot be queried. Nothing to be returned",
+ cpsPath);
+ return false;
+ }
+ return true;
+ }
+
private static void throwDataValidationException(final String details) {
throw new DataValidationException("Invalid Query Parameter.", details);
}
diff --git a/cps-service/src/main/java/org/onap/cps/utils/ValidQueryProperties.java b/cps-service/src/main/java/org/onap/cps/utils/ValidQueryProperties.java
new file mode 100644
index 0000000000..1d7ccb91df
--- /dev/null
+++ b/cps-service/src/main/java/org/onap/cps/utils/ValidQueryProperties.java
@@ -0,0 +1,36 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * 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.utils;
+
+import lombok.Getter;
+
+@Getter
+public enum ValidQueryProperties {
+ HAS_ALL_PROPERTIES("hasAllProperties"),
+ HAS_ALL_MODULES("hasAllModules"),
+ WITH_CPS_PATH("cmHandleWithCpsPath");
+
+ private final String queryProperty;
+
+ ValidQueryProperties(final String queryProperty) {
+ this.queryProperty = queryProperty;
+ }
+}
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy
index a9b04c1ced..d5dcb7fc5c 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy
@@ -88,4 +88,29 @@ class CmHandleQueryRestParametersValidatorSpec extends Specification {
'invalid value' | [moduleName: '']
'invalid name' | [wrongName: 'value']
}
+
+ def 'Validate CmHandle where an exception is thrown due to #scenario.'() {
+ when: 'the validator is called on a cps path condition property'
+ CmHandleQueryRestParametersValidator.validateCpsPathConditionProperties(conditionProperty)
+ then: 'a data validation exception is thrown'
+ def e = thrown(DataValidationException)
+ and: 'exception message matches the expected message'
+ e.details.contains(exceptionMessage)
+ where:
+ scenario | conditionProperty || exceptionMessage
+ 'more than one condition is supplied' | ['cpsPath':'some-path', 'cpsPath2':'some-path'] || 'Only one condition property is allowed for the CPS path query.'
+ 'cpsPath key not supplied' | ['wrong-key':'some-path'] || 'Wrong CPS path condition property. - expecting "cpsPath" as the condition property.'
+ 'cpsPath not supplied' | ['cpsPath':''] || 'Wrong CPS path. - please supply a valid CPS path.'
+ }
+
+ def 'Validate CmHandle where #scenario.'() {
+ when: 'the validator is called on a cps path condition property'
+ def result = CmHandleQueryRestParametersValidator.validateCpsPathConditionProperties(['cpsPath':cpsPath])
+ then: 'the expected boolean value is returned'
+ result == expectedBoolean
+ where:
+ scenario | cpsPath || expectedBoolean
+ 'cpsPath is valid' | '/some/valid/path' || true
+ 'cpsPath attempts to query private properties' | "//additional-properties[@some-property='some-value']" || false
+ }
}