summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps-application/src/main/resources/application.yml2
-rw-r--r--cps-ncmp-rest/pom.xml2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java46
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java10
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java71
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java37
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java25
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java42
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java36
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy102
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy8
-rw-r--r--cps-ncmp-service/src/test/resources/dataOperationRequest.json7
-rw-r--r--cps-ncmp-service/src/test/resources/dataOperationResponseEvent.json1
-rw-r--r--cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy38
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java12
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java4
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java2
-rwxr-xr-xcps-service/src/main/java/org/onap/cps/api/CpsAdminService.java9
-rwxr-xr-xcps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java7
-rw-r--r--cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java12
-rwxr-xr-xcps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java9
-rwxr-xr-xcps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy7
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy13
-rwxr-xr-xdocs/release-notes.rst1
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy2
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAdminServiceIntegrationSpec.groovy34
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy11
-rw-r--r--integration-test/src/test/resources/data/tree/new-test-tree.json12
-rw-r--r--integration-test/src/test/resources/data/tree/new-test-tree.yang21
-rw-r--r--integration-test/src/test/resources/data/tree/updated-test-tree.json10
-rw-r--r--integration-test/src/test/resources/data/tree/updated-test-tree.yang33
31 files changed, 564 insertions, 62 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index a18de2acdb..6aefda9c39 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -109,6 +109,8 @@ app:
dmi:
cm-events:
topic: ${DMI_CM_EVENTS_TOPIC:dmi-cm-events}
+ device-heartbeat:
+ topic: ${DMI_DEVICE_HEARTBEAT_TOPIC:dmi-device-heartbeat}
notification:
diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml
index 63c5f16d04..e60e17418b 100644
--- a/cps-ncmp-rest/pom.xml
+++ b/cps-ncmp-rest/pom.xml
@@ -34,7 +34,7 @@
<artifactId>cps-ncmp-rest</artifactId>
<properties>
- <minimum-coverage>0.99</minimum-coverage>
+ <minimum-coverage>1.00</minimum-coverage>
</properties>
<dependencies>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java
new file mode 100644
index 0000000000..816fc50675
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java
@@ -0,0 +1,46 @@
+/*
+ * ============LICENSE_START========================================================
+ * Copyright (C) 2023 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.impl.config.embeddedcache;
+
+import com.hazelcast.collection.ISet;
+import com.hazelcast.config.SetConfig;
+import org.onap.cps.cache.HazelcastCacheConfig;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class TrustLevelCacheConfig extends HazelcastCacheConfig {
+
+ private static final SetConfig untrustworthyCmHandlesSetConfig = createSetConfig("untrustworthyCmHandlesSetConfig");
+
+ /**
+ * Untrustworthy cmhandle set instance.
+ *
+ * @return instance of distributed set of untrustworthy cmhandles.
+ */
+ @Bean
+ public ISet<String> untrustworthyCmHandlesSet() {
+ return createHazelcastInstance("untrustworthyCmHandlesSet", untrustworthyCmHandlesSetConfig).getSet(
+ "untrustworthyCmHandlesSet");
+ }
+
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
index 02de9854ef..ba6f89192d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
@@ -261,22 +261,22 @@ public class DmiDataOperations extends DmiOperations {
final String topicName = dataOperationResourceUrlParameters.get("topic").get(0);
final String requestId = dataOperationResourceUrlParameters.get("requestId").get(0);
- final MultiValueMap<String, Map<NcmpEventResponseCode, List<String>>>
- cmHandleIdsPerResponseCodesPerOperationId = new LinkedMultiValueMap<>();
+ final MultiValueMap<DmiDataOperation, Map<NcmpEventResponseCode, List<String>>>
+ cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
dmiDataOperationRequestBodies.forEach(dmiDataOperationRequestBody -> {
final List<String> cmHandleIds = dmiDataOperationRequestBody.getCmHandles().stream()
.map(CmHandle::getId).collect(Collectors.toList());
if (throwable.getCause() instanceof HttpClientRequestException) {
- cmHandleIdsPerResponseCodesPerOperationId.add(dmiDataOperationRequestBody.getOperationId(),
+ cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody,
Map.of(NcmpEventResponseCode.UNABLE_TO_READ_RESOURCE_DATA, cmHandleIds));
} else {
- cmHandleIdsPerResponseCodesPerOperationId.add(dmiDataOperationRequestBody.getOperationId(),
+ cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody,
Map.of(NcmpEventResponseCode.DMI_SERVICE_NOT_RESPONDING, cmHandleIds));
}
});
ResourceDataOperationRequestUtils.publishErrorMessageToClientTopic(topicName, requestId,
- cmHandleIdsPerResponseCodesPerOperationId);
+ cmHandleIdsPerResponseCodesPerOperation);
}
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java
new file mode 100644
index 0000000000..458c1b8518
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java
@@ -0,0 +1,71 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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.impl.trustlevel;
+
+import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent;
+
+import com.hazelcast.collection.ISet;
+import io.cloudevents.CloudEvent;
+import io.cloudevents.kafka.impl.KafkaHeaders;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DeviceHeartbeatConsumer {
+
+ private final ISet<String> untrustworthyCmHandlesSet;
+
+ /**
+ * Listening the device heartbeats.
+ *
+ * @param deviceHeartbeatConsumerRecord Device Heartbeat record.
+ */
+ @KafkaListener(topics = "${app.dmi.device-heartbeat.topic}",
+ containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory")
+ public void heartbeatListener(final ConsumerRecord<String, CloudEvent> deviceHeartbeatConsumerRecord) {
+
+ final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(), "ce_id");
+
+ final DeviceTrustLevel deviceTrustLevel =
+ toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class);
+
+ if (deviceTrustLevel == null || deviceTrustLevel.getTrustLevel() == null) {
+ log.warn("No or Invalid trust level defined");
+ return;
+ }
+
+ if (deviceTrustLevel.getTrustLevel().equals(TrustLevel.NONE)) {
+ untrustworthyCmHandlesSet.add(cmHandleId);
+ log.debug("Added cmHandleId to untrustworthy set : {}", cmHandleId);
+ } else if (deviceTrustLevel.getTrustLevel().equals(TrustLevel.COMPLETE) && untrustworthyCmHandlesSet.contains(
+ cmHandleId)) {
+ untrustworthyCmHandlesSet.remove(cmHandleId);
+ log.debug("Removed cmHandleId from untrustworthy set : {}", cmHandleId);
+ }
+ }
+
+}
+
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java
new file mode 100644
index 0000000000..2ed4e45220
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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.impl.trustlevel;
+
+import java.io.Serializable;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@AllArgsConstructor
+@Data
+@NoArgsConstructor
+class DeviceTrustLevel implements Serializable {
+
+ private static final long serialVersionUID = -1705715024067165212L;
+
+ private TrustLevel trustLevel;
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java
new file mode 100644
index 0000000000..f4254bb473
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java
@@ -0,0 +1,25 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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.impl.trustlevel;
+
+public enum TrustLevel {
+ NONE, COMPLETE;
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java
index 2d9a51b844..65cda94787 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java
@@ -30,6 +30,7 @@ import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.NcmpEventResponseCode;
import org.onap.cps.ncmp.api.impl.events.NcmpCloudEventBuilder;
+import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation;
import org.onap.cps.ncmp.events.async1_0_0.Data;
import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent;
import org.onap.cps.ncmp.events.async1_0_0.Response;
@@ -44,49 +45,48 @@ public class DataOperationEventCreator {
*
* @param clientTopic topic the client wants to use for responses
* @param requestId unique identifier per request
- * @param cmHandleIdsPerResponseCodesPerOperationId map of cm handles per operation response per response code
+ * @param cmHandleIdsPerResponseCodesPerOperation map of cm handles per operation response per response code
* @return Cloud Event
*/
public static CloudEvent createDataOperationEvent(final String clientTopic,
final String requestId,
- final MultiValueMap<String,
+ final MultiValueMap<DmiDataOperation,
Map<NcmpEventResponseCode, List<String>>>
- cmHandleIdsPerResponseCodesPerOperationId) {
+ cmHandleIdsPerResponseCodesPerOperation) {
final DataOperationEvent dataOperationEvent = new DataOperationEvent();
- final Data data = createPayloadFromDataOperationResponses(cmHandleIdsPerResponseCodesPerOperationId);
+ final Data data = createPayloadFromDataOperationResponses(cmHandleIdsPerResponseCodesPerOperation);
dataOperationEvent.setData(data);
final Map<String, String> extensions = createDataOperationExtensions(requestId, clientTopic);
return NcmpCloudEventBuilder.builder().type(DataOperationEvent.class.getName())
.event(dataOperationEvent).extensions(extensions).setCloudEvent().build();
}
- private static Data createPayloadFromDataOperationResponses(final MultiValueMap<String, Map<NcmpEventResponseCode,
- List<String>>> cmHandleIdsPerOperationIdPerResponseCode) {
+ private static Data createPayloadFromDataOperationResponses(final MultiValueMap<DmiDataOperation,
+ Map<NcmpEventResponseCode, List<String>>> cmHandleIdsPerResponseCodesPerOperation) {
final Data data = new Data();
final List<org.onap.cps.ncmp.events.async1_0_0.Response> responses = new ArrayList<>();
- cmHandleIdsPerOperationIdPerResponseCode.entrySet().forEach(cmHandleIdsPerOperationIdPerResponseCodeEntries ->
- cmHandleIdsPerOperationIdPerResponseCodeEntries.getValue().forEach(cmHandleIdsPerResponseCodeEntries ->
+ cmHandleIdsPerResponseCodesPerOperation.forEach((dmiDataOperation, cmHandleIdsPerResponseCodes) ->
+ cmHandleIdsPerResponseCodes.forEach(cmHandleIdsPerResponseCodeEntries ->
responses.addAll(createResponseFromDataOperationResponses(
- cmHandleIdsPerOperationIdPerResponseCodeEntries.getKey(),
- cmHandleIdsPerResponseCodeEntries)
- )));
+ dmiDataOperation, cmHandleIdsPerResponseCodeEntries))));
data.setResponses(responses);
return data;
}
private static List<Response> createResponseFromDataOperationResponses(
- final String operationId,
+ final DmiDataOperation dmiDataOperation,
final Map<NcmpEventResponseCode, List<String>> cmHandleIdsPerResponseCodeEntries) {
final List<org.onap.cps.ncmp.events.async1_0_0.Response> responses = new ArrayList<>();
- cmHandleIdsPerResponseCodeEntries.entrySet()
- .forEach(cmHandleIdsPerResponseCodeEntry -> {
- final Response response = new Response();
- response.setOperationId(operationId);
- response.setStatusCode(cmHandleIdsPerResponseCodeEntry.getKey().getStatusCode());
- response.setStatusMessage(cmHandleIdsPerResponseCodeEntry.getKey().getStatusMessage());
- response.setIds(cmHandleIdsPerResponseCodeEntry.getValue());
- responses.add(response);
- });
+ cmHandleIdsPerResponseCodeEntries.forEach((ncmpEventResponseCode, cmHandleIds) -> {
+ final Response response = new Response();
+ response.setOperationId(dmiDataOperation.getOperationId());
+ response.setStatusCode(ncmpEventResponseCode.getStatusCode());
+ response.setStatusMessage(ncmpEventResponseCode.getStatusMessage());
+ response.setIds(cmHandleIds);
+ response.setResourceIdentifier(dmiDataOperation.getResourceIdentifier());
+ response.setOptions(dmiDataOperation.getOptions());
+ responses.add(response);
+ });
return responses;
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java
index d8fb904f21..c455337ac3 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java
@@ -68,8 +68,8 @@ public class ResourceDataOperationRequestUtils {
final Collection<YangModelCmHandle> yangModelCmHandles) {
final Map<String, List<DmiDataOperation>> dmiDataOperationsOutPerDmiServiceName = new HashMap<>();
- final MultiValueMap<String, Map<NcmpEventResponseCode, List<String>>> cmHandleIdsPerResponseCodesPerOperationId
- = new LinkedMultiValueMap<>();
+ final MultiValueMap<DmiDataOperation, Map<NcmpEventResponseCode,
+ List<String>>> cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
final Set<String> nonReadyCmHandleIdsLookup = filterAndGetNonReadyCmHandleIds(yangModelCmHandles);
final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName =
@@ -100,15 +100,15 @@ public class ResourceDataOperationRequestUtils {
}
}
}
- populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperationId,
- dataOperationDefinitionIn.getOperationId(), NcmpEventResponseCode.CM_HANDLES_NOT_FOUND,
- nonExistingCmHandleIds);
- populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperationId,
- dataOperationDefinitionIn.getOperationId(), NcmpEventResponseCode.CM_HANDLES_NOT_READY,
- nonReadyCmHandleIds);
+ populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
+ DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
+ NcmpEventResponseCode.CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds);
+ populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
+ DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
+ NcmpEventResponseCode.CM_HANDLES_NOT_READY, nonReadyCmHandleIds);
}
- if (!cmHandleIdsPerResponseCodesPerOperationId.isEmpty()) {
- publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperationId);
+ if (!cmHandleIdsPerResponseCodesPerOperation.isEmpty()) {
+ publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation);
}
return dmiDataOperationsOutPerDmiServiceName;
}
@@ -118,16 +118,16 @@ public class ResourceDataOperationRequestUtils {
*
* @param clientTopic client given topic
* @param requestId unique identifier per request
- * @param cmHandleIdsPerResponseCodesPerOperationId list of cm handle ids per operation id with response code
+ * @param cmHandleIdsPerResponseCodesPerOperation list of cm handle ids per operation with response code
*/
@Async
public static void publishErrorMessageToClientTopic(final String clientTopic,
final String requestId,
- final MultiValueMap<String,
+ final MultiValueMap<DmiDataOperation,
Map<NcmpEventResponseCode, List<String>>>
- cmHandleIdsPerResponseCodesPerOperationId) {
+ cmHandleIdsPerResponseCodesPerOperation) {
final CloudEvent dataOperationCloudEvent = DataOperationEventCreator.createDataOperationEvent(clientTopic,
- requestId, cmHandleIdsPerResponseCodesPerOperationId);
+ requestId, cmHandleIdsPerResponseCodesPerOperation);
final EventsPublisher<CloudEvent> eventsPublisher = CpsApplicationContext.getCpsBean(EventsPublisher.class);
eventsPublisher.publishCloudEvent(clientTopic, requestId, dataOperationCloudEvent);
}
@@ -174,14 +174,14 @@ public class ResourceDataOperationRequestUtils {
!= CmHandleState.READY).map(YangModelCmHandle::getId).collect(Collectors.toSet());
}
- private static void populateCmHandleIdsPerOperationIdPerResponseCode(final MultiValueMap<String,
- Map<NcmpEventResponseCode, List<String>>> cmHandleIdsPerResponseCodesPerOperationId,
- final String operationId,
+ private static void populateCmHandleIdsPerOperationIdPerResponseCode(final MultiValueMap<DmiDataOperation,
+ Map<NcmpEventResponseCode, List<String>>> cmHandleIdsPerResponseCodesPerOperation,
+ final DmiDataOperation dmiDataOperation,
final NcmpEventResponseCode
ncmpEventResponseCode,
final List<String> cmHandleIds) {
if (!cmHandleIds.isEmpty()) {
- cmHandleIdsPerResponseCodesPerOperationId.add(operationId, Map.of(ncmpEventResponseCode, cmHandleIds));
+ cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperation, Map.of(ncmpEventResponseCode, cmHandleIds));
}
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy
new file mode 100644
index 0000000000..48de23dca2
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy
@@ -0,0 +1,102 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 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.impl.trustlevel
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.hazelcast.collection.ISet
+import io.cloudevents.CloudEvent
+import io.cloudevents.core.builder.CloudEventBuilder
+import org.apache.kafka.clients.consumer.ConsumerRecord
+import org.onap.cps.utils.JsonObjectMapper
+import org.springframework.boot.test.context.SpringBootTest
+import spock.lang.Specification
+
+@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper])
+class DeviceHeartbeatConsumerSpec extends Specification {
+
+ def mockUntrustworthyCmHandlesSet = Mock(ISet<String>)
+ def objectMapper = new ObjectMapper()
+
+ def objectUnderTest = new DeviceHeartbeatConsumer(mockUntrustworthyCmHandlesSet)
+
+ def 'Operations to be done in an empty untrustworthy set for #scenario'() {
+ given: 'an event with trustlevel as #trustLevel'
+ def incomingEvent = testCloudEvent(trustLevel)
+ and: 'transformed as a kafka record'
+ def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'cmhandle1', incomingEvent)
+ consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1'))
+ when: 'the event is consumed'
+ objectUnderTest.heartbeatListener(consumerRecord)
+ then: 'untrustworthy cmhandles are stored'
+ untrustworthyCmHandlesSetInvocationForAdd * mockUntrustworthyCmHandlesSet.add(_)
+ and: 'trustworthy cmHandles will be removed from untrustworthy set'
+ untrustworthyCmHandlesSetInvocationForContains * mockUntrustworthyCmHandlesSet.contains(_)
+
+ where: 'below scenarios are applicable'
+ scenario | trustLevel || untrustworthyCmHandlesSetInvocationForAdd | untrustworthyCmHandlesSetInvocationForContains
+ 'None trust' | TrustLevel.NONE || 1 | 0
+ 'Complete trust' | TrustLevel.COMPLETE || 0 | 1
+ }
+
+ def 'Invalid trust'() {
+ when: 'we provide an invalid trust in the event'
+ def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'cmhandle1', testCloudEvent(null))
+ consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1'))
+ objectUnderTest.heartbeatListener(consumerRecord)
+ then: 'no interaction with the untrustworthy cmhandles set'
+ 0 * mockUntrustworthyCmHandlesSet.add(_)
+ 0 * mockUntrustworthyCmHandlesSet.contains(_)
+ 0 * mockUntrustworthyCmHandlesSet.remove(_)
+ and: 'control flow returns without any exception'
+ noExceptionThrown()
+
+ }
+
+ def 'Remove trustworthy cmhandles from untrustworthy cmhandles set'() {
+ given: 'an event with COMPLETE trustlevel'
+ def incomingEvent = testCloudEvent(TrustLevel.COMPLETE)
+ and: 'transformed as a kafka record'
+ def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'cmhandle1', incomingEvent)
+ consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1'))
+ and: 'untrustworthy cmhandles set contains cmhandle1'
+ 1 * mockUntrustworthyCmHandlesSet.contains(_) >> true
+ when: 'the event is consumed'
+ objectUnderTest.heartbeatListener(consumerRecord)
+ then: 'cmhandle removed from untrustworthy cmhandles set'
+ 1 * mockUntrustworthyCmHandlesSet.remove(_) >> {
+ args ->
+ {
+ args[0].equals('cmhandle1')
+ }
+ }
+
+ }
+
+ def testCloudEvent(trustLevel) {
+ return CloudEventBuilder.v1().withData(objectMapper.writeValueAsBytes(new DeviceTrustLevel(trustLevel)))
+ .withId("cmhandle1")
+ .withSource(URI.create('DMI'))
+ .withDataSchema(URI.create('test'))
+ .withType('org.onap.cm.events.trustlevel-notification')
+ .build()
+ }
+
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy
index c866824062..38b2056d00 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy
@@ -110,9 +110,9 @@ class ResourceDataOperationRequestUtilsSpec extends MessagingBaseSpec {
toTargetEvent(consumerRecordOut.value(), DataOperationEvent.class)
and: 'data operation response event response size is 3'
dataOperationResponseEvent.data.responses.size() == 3
- and: 'verify published response data as json string'
- jsonObjectMapper.asJsonString(dataOperationResponseEvent.data.responses)
- == '[{"operationId":"operational-14","ids":["unknown-cm-handle"],"statusCode":"100","statusMessage":"cm handle id(s) not found"},{"operationId":"operational-14","ids":["non-ready-cm handle"],"statusCode":"101","statusMessage":"cm handle(s) not ready"},{"operationId":"running-12","ids":["non-ready-cm handle"],"statusCode":"101","statusMessage":"cm handle(s) not ready"}]'
+ and: 'verify published data operation response as json string'
+ def dataOperationResponseEventJson = TestUtils.getResourceFileContent('dataOperationResponseEvent.json')
+ jsonObjectMapper.asJsonString(dataOperationResponseEvent.data.responses) == dataOperationResponseEventJson
}
static def getYangModelCmHandles() {
@@ -126,7 +126,7 @@ class ResourceDataOperationRequestUtilsSpec extends MessagingBaseSpec {
new YangModelCmHandle(id: 'ch3-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
new YangModelCmHandle(id: 'ch4-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
new YangModelCmHandle(id: 'ch7-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
- new YangModelCmHandle(id: 'non-ready-cm handle', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: advisedState)
+ new YangModelCmHandle(id: 'non-ready-cm-handle', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: advisedState)
]
}
}
diff --git a/cps-ncmp-service/src/test/resources/dataOperationRequest.json b/cps-ncmp-service/src/test/resources/dataOperationRequest.json
index d2e0d64892..f69b87631f 100644
--- a/cps-ncmp-service/src/test/resources/dataOperationRequest.json
+++ b/cps-ncmp-service/src/test/resources/dataOperationRequest.json
@@ -10,18 +10,20 @@
"ch3-dmi2",
"unknown-cm-handle",
"ch6-dmi1",
- "non-ready-cm handle"
+ "non-ready-cm-handle"
]
},
{
"operation": "read",
"operationId": "running-12",
"datastore": "ncmp-datastore:passthrough-running",
+ "options": "some option",
+ "resourceIdentifier": "some resource identifier",
"targetIds": [
"ch1-dmi1",
"ch7-dmi2",
"ch2-dmi1",
- "non-ready-cm handle"
+ "non-ready-cm-handle"
]
},
{
@@ -29,6 +31,7 @@
"operationId": "operational-15",
"datastore": "ncmp-datastore:passthrough-operational",
"options": "some option",
+ "resourceIdentifier": "some resource identifier",
"targetIds": [
"ch4-dmi2",
"ch6-dmi1"
diff --git a/cps-ncmp-service/src/test/resources/dataOperationResponseEvent.json b/cps-ncmp-service/src/test/resources/dataOperationResponseEvent.json
new file mode 100644
index 0000000000..611d47d1a3
--- /dev/null
+++ b/cps-ncmp-service/src/test/resources/dataOperationResponseEvent.json
@@ -0,0 +1 @@
+[{"operationId":"operational-14","ids":["unknown-cm-handle"],"resourceIdentifier":"some resource identifier","options":"some option","statusCode":"100","statusMessage":"cm handle id(s) not found"},{"operationId":"operational-14","ids":["non-ready-cm-handle"],"resourceIdentifier":"some resource identifier","options":"some option","statusCode":"101","statusMessage":"cm handle(s) not ready"},{"operationId":"running-12","ids":["non-ready-cm-handle"],"resourceIdentifier":"some resource identifier","options":"some option","statusCode":"101","statusMessage":"cm handle(s) not ready"}] \ No newline at end of file
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
index fd669b75c3..c30a63fd46 100644
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
@@ -181,12 +181,46 @@ class QueryRestControllerSpec extends Specification {
.andReturn().response
then: 'the response contains the the datanode in json format'
assert response.status == HttpStatus.OK.value()
- assert Integer.valueOf(response.getHeaderValue("total-pages")) == expectedPageSize
+ assert Integer.valueOf(response.getHeaderValue("total-pages")) == expectedTotalPageSize
assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement3","leaveListElement4"]}}')
where: 'the following options for include descendants are provided in the request'
- scenario | pageIndex | pageSize | totalAnchors || expectedPageSize
+ scenario | pageIndex | pageSize | totalAnchors || expectedTotalPageSize
'1st page with all anchors' | 1 | 3 | 3 || 1
'1st page with less anchors' | 1 | 2 | 3 || 2
}
+
+ def 'Query data node across all anchors with pagination option with #scenario.'() {
+ given: 'service method returns a list containing a data node from different anchors'
+ def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
+ .withAnchor('my_anchor')
+ .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
+ def dataNode2 = new DataNodeBuilder().withXpath('/xpath')
+ .withAnchor('my_anchor_2')
+ .withLeaves([leaf: 'value', leafList: ['leaveListElement3', 'leaveListElement4']]).build()
+ and: 'the query endpoint'
+ def dataspaceName = 'my_dataspace'
+ def cpsPath = 'some/cps/path'
+ def dataNodeEndpoint = "$basePath/v2/dataspaces/$dataspaceName/nodes/query"
+ mockCpsQueryService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath,
+ INCLUDE_ALL_DESCENDANTS, PaginationOption.NO_PAGINATION) >> [dataNode1, dataNode2]
+ mockCpsQueryService.countAnchorsForDataspaceAndCpsPath(dataspaceName, cpsPath) >> 2
+ when: 'query data nodes API is invoked'
+ def response =
+ mvc.perform(
+ get(dataNodeEndpoint)
+ .param('cps-path', cpsPath)
+ .param('descendants', "all")
+ .param(parameterName, "1"))
+ .andReturn().response
+ then: 'the response contains the the datanode in json format'
+ assert response.status == HttpStatus.OK.value()
+ assert Integer.valueOf(response.getHeaderValue("total-pages")) == 1
+ assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
+ assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement3","leaveListElement4"]}}')
+ where:
+ scenario | parameterName
+ 'only page size' | 'pageSize'
+ 'only page index' | 'pageIndex'
+ }
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
index 6f9f5a482c..847a4a32fe 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
@@ -171,6 +171,18 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
anchorRepository.deleteAllByDataspaceAndNameIn(dataspaceEntity, anchorNames);
}
+ @Transactional
+ @Override
+ public void updateAnchorSchemaSet(final String dataspaceName,
+ final String anchorName,
+ final String schemaSetName) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ final SchemaSetEntity schemaSetEntity = schemaSetRepository
+ .getByDataspaceAndName(dataspaceEntity, schemaSetName);
+ anchorRepository.updateAnchorSchemaSetId(schemaSetEntity.getId(), anchorEntity.getId());
+ }
+
private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) {
final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
index 5bb5857810..b8503a7fea 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
@@ -99,4 +99,8 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Long> {
deleteAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames.toArray(new String[0]));
}
+ @Modifying
+ @Query(value = "UPDATE anchor SET schema_set_id =:schemaSetId WHERE id = :anchorId ", nativeQuery = true)
+ void updateAnchorSchemaSetId(@Param("schemaSetId") int schemaSetId, @Param("anchorId") long anchorId);
+
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java
index 4f056c8f6e..c187f20ea9 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java
@@ -94,7 +94,7 @@ public class FragmentPrefetchRepositoryImpl implements FragmentPrefetchRepositor
final FragmentEntity fragmentEntity = new FragmentEntity();
fragmentEntity.setId(resultSet.getLong("id"));
fragmentEntity.setXpath(resultSet.getString("xpath"));
- fragmentEntity.setParentId(resultSet.getLong("parentId"));
+ fragmentEntity.setParentId(resultSet.getObject("parentId", Long.class));
fragmentEntity.setAttributes(resultSet.getString("attributes"));
fragmentEntity.setAnchor(anchorEntityPerId.get(resultSet.getLong("anchorId")));
fragmentEntity.setChildFragments(new HashSet<>());
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java b/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java
index fcf3f54cce..edd052a51c 100755
--- a/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java
+++ b/cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java
@@ -135,4 +135,13 @@ public interface CpsAdminService {
* given module names
*/
Collection<String> queryAnchorNames(String dataspaceName, Collection<String> moduleNames);
+
+ /**
+ * Update schema set of an anchor.
+ *
+ * @param dataspaceName dataspace name
+ * @param anchorName anchor name
+ * @param schemaSetName schema set name
+ */
+ void updateAnchorSchemaSet(String dataspaceName, String anchorName, String schemaSetName);
}
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
index e286eea173..d83ee434de 100755
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
@@ -120,4 +120,11 @@ public class CpsAdminServiceImpl implements CpsAdminService {
final Collection<Anchor> anchors = cpsAdminPersistenceService.queryAnchors(dataspaceName, moduleNames);
return anchors.stream().map(Anchor::getName).collect(Collectors.toList());
}
+
+ @Override
+ public void updateAnchorSchemaSet(final String dataspaceName,
+ final String anchorName,
+ final String schemaSetName) {
+ cpsAdminPersistenceService.updateAnchorSchemaSet(dataspaceName, anchorName, schemaSetName);
+ }
}
diff --git a/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java b/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java
index 405e6e2a88..067191b5a5 100644
--- a/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java
+++ b/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java
@@ -24,6 +24,7 @@ import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
import com.hazelcast.config.NamedConfig;
import com.hazelcast.config.QueueConfig;
+import com.hazelcast.config.SetConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import lombok.extern.slf4j.Slf4j;
@@ -57,6 +58,10 @@ public class HazelcastCacheConfig {
if (namedConfig instanceof QueueConfig) {
config.addQueueConfig((QueueConfig) namedConfig);
}
+ if (namedConfig instanceof SetConfig) {
+ config.addSetConfig((SetConfig) namedConfig);
+ }
+
config.setClusterName(clusterName);
updateDiscoveryMode(config);
return config;
@@ -76,6 +81,13 @@ public class HazelcastCacheConfig {
return commonQueueConfig;
}
+ protected static SetConfig createSetConfig(final String configName) {
+ final SetConfig commonSetConfig = new SetConfig(configName);
+ commonSetConfig.setBackupCount(1);
+ commonSetConfig.setAsyncBackupCount(1);
+ return commonSetConfig;
+ }
+
protected void updateDiscoveryMode(final Config config) {
if (cacheKubernetesEnabled) {
log.info("Enabling kubernetes mode with service-name : {}", cacheKubernetesServiceName);
diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
index 1c1e80a20f..5a1810f473 100755
--- a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
@@ -133,4 +133,13 @@ public interface CpsAdminPersistenceService {
* @param anchorNames anchor names
*/
void deleteAnchors(String dataspaceName, Collection<String> anchorNames);
+
+ /**
+ * Delete anchors by name in given dataspace.
+ *
+ * @param dataspaceName dataspace name
+ * @param anchorName anchor name
+ * @param schemaSetName schema set name
+ */
+ void updateAnchorSchemaSet(String dataspaceName, String anchorName, String schemaSetName);
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
index eb41e2085f..12564fb6d4 100755
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
@@ -178,4 +178,11 @@ class CpsAdminServiceImplSpec extends Specification {
and: 'the CpsValidator is called on the dataspaceName'
1 * mockCpsValidator.validateNameCharacters('someDataspace')
}
+
+ def 'Update anchor schema set.'() {
+ when: 'update anchor is invoked'
+ objectUnderTest.updateAnchorSchemaSet('someDataspace', 'someAnchor', 'someSchemaSetName')
+ then: 'associated persistence service method is invoked with correct parameter'
+ 1 * mockCpsAdminPersistenceService.updateAnchorSchemaSet('someDataspace', 'someAnchor', 'someSchemaSetName')
+ }
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy
index 8efd48547e..415e9fd496 100644
--- a/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy
@@ -45,10 +45,17 @@ class HazelcastCacheConfigSpec extends Specification {
} else {
assert result.config.queueConfigs.isEmpty()
}
+ and: 'if applicable it has a set config with the expected name'
+ if (expectSetConfig) {
+ assert result.config.setConfigs.values()[0].name == 'my set config'
+ } else {
+ assert result.config.setConfigs.isEmpty()
+ }
where: 'the following configs are used'
- scenario | config || expectMapConfig | expectQueueConfig
- 'Map Config' | HazelcastCacheConfig.createMapConfig('my map config') || true | false
- 'Queue Config' | HazelcastCacheConfig.createQueueConfig('my queue config') || false | true
+ scenario | config || expectMapConfig | expectQueueConfig | expectSetConfig
+ 'Map Config' | HazelcastCacheConfig.createMapConfig('my map config') || true | false | false
+ 'Queue Config' | HazelcastCacheConfig.createQueueConfig('my queue config') || false | true | false
+ 'Set Config' | HazelcastCacheConfig.createSetConfig('my set config') || false | false | true
}
}
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index cd70cf8be7..3f672ad626 100755
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -39,6 +39,7 @@ Release Data
Bug Fixes
---------
3.3.6
+ - `CPS-1841 <https://jira.onap.org/browse/CPS-1841>`_ Update of top-level data node fails with exception
Features
--------
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
index 4780e36428..03ef9c2fdc 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
@@ -108,7 +108,7 @@ class CpsIntegrationSpecBase extends Specification {
def dataspaceExists(dataspaceName) {
try {
cpsAdminService.getDataspace(dataspaceName)
- } catch (DataspaceNotFoundException e) {
+ } catch (DataspaceNotFoundException dataspaceNotFoundException) {
return false
}
return true
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAdminServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAdminServiceIntegrationSpec.groovy
index 92fbdaaa25..bdd894c31f 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAdminServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAdminServiceIntegrationSpec.groovy
@@ -22,10 +22,12 @@ package org.onap.cps.integration.functional
import org.onap.cps.api.CpsAdminService
import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.exceptions.AnchorNotFoundException
import org.onap.cps.spi.exceptions.DataspaceInUseException
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
+import java.time.OffsetDateTime
class CpsAdminServiceIntegrationSpec extends CpsIntegrationSpecBase {
@@ -44,8 +46,8 @@ class CpsAdminServiceIntegrationSpec extends CpsIntegrationSpecBase {
def thrown = null
try {
objectUnderTest.getDataspace('newDataspace')
- } catch(Exception e) {
- thrown = e
+ } catch(Exception exception) {
+ thrown = exception
}
assert thrown instanceof DataspaceNotFoundException
}
@@ -100,8 +102,8 @@ class CpsAdminServiceIntegrationSpec extends CpsIntegrationSpecBase {
def thrown = null
try {
objectUnderTest.getAnchor(GENERAL_TEST_DATASPACE, 'newAnchor')
- } catch(Exception e) {
- thrown = e
+ } catch(Exception exception) {
+ thrown = exception
}
assert thrown instanceof AnchorNotFoundException
}
@@ -151,4 +153,28 @@ class CpsAdminServiceIntegrationSpec extends CpsIntegrationSpecBase {
'just unknown module(s)' | GENERAL_TEST_DATASPACE
}
+ def 'Update anchor schema set.'() {
+ when: 'a new schema set with tree yang model is created'
+ def newTreeYangModelAsString = readResourceDataFile('tree/new-test-tree.yang')
+ cpsModuleService.createSchemaSet(GENERAL_TEST_DATASPACE, 'newTreeSchemaSet', [tree: newTreeYangModelAsString])
+ then: 'an anchor with new schema set is created'
+ objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, 'newTreeSchemaSet', 'anchor4')
+ and: 'the new tree datanode is saved'
+ def treeJsonData = readResourceDataFile('tree/new-test-tree.json')
+ cpsDataService.saveData(GENERAL_TEST_DATASPACE, 'anchor4', treeJsonData, OffsetDateTime.now())
+ and: 'saved tree data node can be retrieved by its normalized xpath'
+ def branchName = cpsDataService.getDataNodes(GENERAL_TEST_DATASPACE, 'anchor4', "/test-tree/branch", FetchDescendantsOption.DIRECT_CHILDREN_ONLY)[0].leaves['name']
+ assert branchName == 'left'
+ and: 'a another schema set with updated tree yang model is created'
+ def updatedTreeYangModelAsString = readResourceDataFile('tree/updated-test-tree.yang')
+ cpsModuleService.createSchemaSet(GENERAL_TEST_DATASPACE, 'anotherTreeSchemaSet', [tree: updatedTreeYangModelAsString])
+ and: 'anchor4 schema set is updated with another schema set successfully'
+ objectUnderTest.updateAnchorSchemaSet(GENERAL_TEST_DATASPACE, 'anchor4', 'anotherTreeSchemaSet')
+ when: 'updated tree data node with new leaves'
+ def updatedTreeJsonData = readResourceDataFile('tree/updated-test-tree.json')
+ cpsDataService.updateNodeLeaves(GENERAL_TEST_DATASPACE, "anchor4", "/test-tree/branch[@name='left']", updatedTreeJsonData, OffsetDateTime.now())
+ then: 'updated tree data node can be retrieved by its normalized xpath'
+ def birdsName = cpsDataService.getDataNodes(GENERAL_TEST_DATASPACE, 'anchor4',"/test-tree/branch[@name='left']/nest", FetchDescendantsOption.DIRECT_CHILDREN_ONLY)[0].leaves['birds']
+ assert birdsName as String == '[Raven, Night Owl, Crow]'
+ }
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
index 9716cb56eb..82a415ecef 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
@@ -394,6 +394,17 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
restoreBookstoreDataAnchor(1)
}
+ def 'Update bookstore top-level container data node.'() {
+ when: 'the bookstore top-level container is updated'
+ def json = '{ "bookstore": { "bookstore-name": "new bookstore" }}'
+ objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/', json, now)
+ then: 'bookstore name has been updated'
+ def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', DIRECT_CHILDREN_ONLY)
+ result.leaves.'bookstore-name'[0] == 'new bookstore'
+ cleanup:
+ restoreBookstoreDataAnchor(1)
+ }
+
def 'Update multiple data node leaves.'() {
given: 'Updated json for bookstore data'
def jsonData = "{'book-store:books':{'lang':'English/French','price':100,'title':'Matilda'}}"
diff --git a/integration-test/src/test/resources/data/tree/new-test-tree.json b/integration-test/src/test/resources/data/tree/new-test-tree.json
new file mode 100644
index 0000000000..f7aefc472d
--- /dev/null
+++ b/integration-test/src/test/resources/data/tree/new-test-tree.json
@@ -0,0 +1,12 @@
+{
+ "test-tree": {
+ "branch": [
+ {
+ "name": "left",
+ "nest": {
+ "name": "small"
+ }
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/integration-test/src/test/resources/data/tree/new-test-tree.yang b/integration-test/src/test/resources/data/tree/new-test-tree.yang
new file mode 100644
index 0000000000..1a08b92f14
--- /dev/null
+++ b/integration-test/src/test/resources/data/tree/new-test-tree.yang
@@ -0,0 +1,21 @@
+module test-tree {
+ yang-version 1.1;
+
+ namespace "org:onap:cps:test:test-tree";
+ prefix tree;
+ revision "2020-02-02";
+
+ container test-tree {
+ list branch {
+ key "name";
+ leaf name {
+ type string;
+ }
+ container nest {
+ leaf name {
+ type string;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/integration-test/src/test/resources/data/tree/updated-test-tree.json b/integration-test/src/test/resources/data/tree/updated-test-tree.json
new file mode 100644
index 0000000000..2c2eea4f0d
--- /dev/null
+++ b/integration-test/src/test/resources/data/tree/updated-test-tree.json
@@ -0,0 +1,10 @@
+{
+ "nest": {
+ "name": "small",
+ "birds": [
+ "Night Owl",
+ "Raven",
+ "Crow"
+ ]
+ }
+} \ No newline at end of file
diff --git a/integration-test/src/test/resources/data/tree/updated-test-tree.yang b/integration-test/src/test/resources/data/tree/updated-test-tree.yang
new file mode 100644
index 0000000000..bd883e8b6d
--- /dev/null
+++ b/integration-test/src/test/resources/data/tree/updated-test-tree.yang
@@ -0,0 +1,33 @@
+module test-tree {
+ yang-version 1.1;
+
+ namespace "org:onap:cps:test:test-tree";
+ prefix tree;
+
+ revision "2023-08-17" {
+ description
+ "added list of birds to nest";
+ }
+
+ revision "2020-09-15" {
+ description
+ "Sample Model";
+ }
+
+ container test-tree {
+ list branch {
+ key "name";
+ leaf name {
+ type string;
+ }
+ container nest {
+ leaf name {
+ type string;
+ }
+ leaf-list birds {
+ type string;
+ }
+ }
+ }
+ }
+} \ No newline at end of file