*
* @param cmHandleId network resource identifier
* @param requestId requestId for async responses
+ * @param options options field for filtered response
* @return {@code ResponseEntity} response entity
*/
- public ResponseEntity<Object> getAllResourceDataFromDmi(final String cmHandleId, final String requestId) {
+ public ResponseEntity<Object> getAllResourceDataFromDmi(final String cmHandleId, final String requestId,
+ final String options) {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(
- PASSTHROUGH_OPERATIONAL.getDatastoreName(), yangModelCmHandle, "/", null,
+ PASSTHROUGH_OPERATIONAL.getDatastoreName(), yangModelCmHandle, "/", options,
null);
return dmiRestClient.synchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody, READ,
DmiRestClient.NO_AUTHORIZATION);
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2024 Nordix Foundation
+ * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import com.hazelcast.map.IMap;
import java.time.OffsetDateTime;
+import java.util.Collection;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsModuleService;
import org.onap.cps.ncmp.api.inventory.DataStoreSyncState;
import org.onap.cps.ncmp.api.inventory.models.CompositeState;
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
private static final boolean DATA_SYNC_DONE = true;
private final InventoryPersistence inventoryPersistence;
-
+ private final CpsModuleService cpsModuleService;
private final CpsDataService cpsDataService;
-
private final ModuleOperationsUtils moduleOperationsUtils;
-
private final IMap<String, Boolean> dataSyncSemaphores;
/**
*/
@Scheduled(initialDelayString = "${ncmp.timers.cm-handle-data-sync.initial-delay-ms:40000}",
fixedDelayString = "${ncmp.timers.cm-handle-data-sync.sleep-time-ms:30000}")
- public void executeUnSynchronizedReadyCmHandlePoll() {
- moduleOperationsUtils.getUnsynchronizedReadyCmHandles().forEach(unSynchronizedReadyCmHandle -> {
- final String cmHandleId = unSynchronizedReadyCmHandle.getId();
- if (hasPushedIntoSemaphoreMap(cmHandleId)) {
- log.info("Executing data sync on {}", cmHandleId);
- final CompositeState compositeState = inventoryPersistence
- .getCmHandleState(cmHandleId);
- final String resourceData = moduleOperationsUtils.getResourceData(cmHandleId);
- if (resourceData == null) {
- log.error("Error retrieving resource data for Cm-Handle: {}", cmHandleId);
- } else {
- cpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId,
- resourceData, OffsetDateTime.now());
- setSyncStateToSynchronized().accept(compositeState);
- inventoryPersistence.saveCmHandleState(cmHandleId, compositeState);
- updateDataSyncSemaphoreMap(cmHandleId);
- log.info("Data sync finished for {}", cmHandleId);
- }
- } else {
- log.info("{} already processed by another instance", cmHandleId);
+ public void executeUnsynchronizedReadyCmHandleForInitialDataSync() {
+ final List<YangModelCmHandle> unsynchronizedReadyCmHandles =
+ moduleOperationsUtils.getUnsynchronizedReadyCmHandles();
+ unsynchronizedReadyCmHandles.stream().map(YangModelCmHandle::getId).forEach(this::synchronizeCmHandle);
+ }
+
+ private void synchronizeCmHandle(final String cmHandleId) {
+
+ if (!hasPushedIntoSemaphoreMap(cmHandleId)) {
+ log.debug("{} already processed by another instance", cmHandleId);
+ return;
+ }
+
+ try {
+ performDataSynchronizationForCmHandle(cmHandleId);
+ } catch (final Exception exception) {
+ log.error("Failed to complete data sync for CM handle: {}", cmHandleId, exception);
+ }
+ }
+
+ private void performDataSynchronizationForCmHandle(final String cmHandleId) {
+ log.info("Executing data sync on {}", cmHandleId);
+ final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId);
+ final Collection<String> rootNodeReferences =
+ cpsModuleService.getRootNodeReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
+
+ for (final String rootNodeReference : rootNodeReferences) {
+ synchronizeRootNodeReferences(cmHandleId, rootNodeReference);
+ }
+
+ setSyncStateToSynchronized().accept(compositeState);
+ inventoryPersistence.saveCmHandleState(cmHandleId, compositeState);
+ updateDataSyncSemaphoreMap(cmHandleId);
+ log.info("Data sync finished for {}", cmHandleId);
+ }
+
+ private void synchronizeRootNodeReferences(final String cmHandleId, final String rootNodeReference) {
+ final String options = String.format("(fields=%s)", rootNodeReference);
+
+ try {
+ final String resourceData = moduleOperationsUtils.getResourceData(cmHandleId, options);
+ if (resourceData == null) {
+ log.warn("No resource data found for CM handle: {} with options: {}", cmHandleId, options);
+ return;
}
- });
- log.debug("No Cm-Handles currently found in READY State and Operational Sync State is UNSYNCHRONIZED");
+ cpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, resourceData,
+ OffsetDateTime.now());
+ } catch (final Exception exception) {
+ log.error("Failed to sync module and root node for CM handle: {} with options: {}", cmHandleId, options,
+ exception);
+ }
}
private Consumer<CompositeState> setSyncStateToSynchronized() {
private final CmHandleQueryService cmHandleQueryService;
private final DmiDataOperations dmiDataOperations;
private final JsonObjectMapper jsonObjectMapper;
+
private static final String RETRY_ATTEMPT_KEY = "attempt";
private static final String MODULE_SET_TAG_KEY = "moduleSetTag";
public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: %s";
* @param cmHandleId cm handle id
* @return optional string containing the resource data
*/
- public String getResourceData(final String cmHandleId) {
+ public String getResourceData(final String cmHandleId, final String options) {
final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getAllResourceDataFromDmi(
- cmHandleId, UUID.randomUUID().toString());
+ cmHandleId, UUID.randomUUID().toString(), options);
if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) {
return getFirstResource(resourceDataResponseEntity.getBody());
}
mockYangModelCmHandleRetrieval([yangModelCmHandleProperty], 'my-module-set-tag')
and: 'a positive response from DMI service when it is called with the expected parameters'
def responseFromDmi = new ResponseEntity<Object>(HttpStatus.OK)
- def expectedTemplateWithVariables = new UrlTemplateParameters('myServiceName/dmi/v1/ch/{cmHandleId}/data/ds/{datastore}?resourceIdentifier={resourceIdentifier}', ['resourceIdentifier': '/', 'datastore': 'ncmp-datastore:passthrough-operational', 'cmHandleId': cmHandleId])
+ def expectedTemplateWithVariables = new UrlTemplateParameters('myServiceName/dmi/v1/ch/{cmHandleId}/data/ds/{datastore}?resourceIdentifier={resourceIdentifier}&options={options}', ['resourceIdentifier': '/', 'datastore': 'ncmp-datastore:passthrough-operational', 'cmHandleId': cmHandleId, 'options': OPTIONS_PARAM])
def expectedJson = '{"operation":"read","cmHandleProperties":{"prop1":"val1"},"moduleSetTag":"my-module-set-tag"}'
mockDmiRestClient.synchronousPostOperationWithJsonData(DATA, expectedTemplateWithVariables, expectedJson, READ, null) >> responseFromDmi
when: 'get resource data is invoked'
- def result = objectUnderTest.getAllResourceDataFromDmi(cmHandleId, NO_REQUEST_ID)
+ def result = objectUnderTest.getAllResourceDataFromDmi(cmHandleId, NO_REQUEST_ID, OPTIONS_PARAM)
then: 'the result is the response from the DMI service'
assert result == responseFromDmi
}
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import com.hazelcast.map.IMap
import org.onap.cps.api.CpsDataService
+import org.onap.cps.api.CpsModuleService
import org.onap.cps.ncmp.api.inventory.models.CompositeState
import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
class DataSyncWatchdogSpec extends Specification {
def mockInventoryPersistence = Mock(InventoryPersistence)
-
+ def mockCpsModuleService = Mock(CpsModuleService)
def mockCpsDataService = Mock(CpsDataService)
-
- def mockSyncUtils = Mock(ModuleOperationsUtils)
-
+ def mockModuleOperationUtils = Mock(ModuleOperationsUtils)
def mockDataSyncSemaphores = Mock(IMap<String,Boolean>)
def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
- def objectUnderTest = new DataSyncWatchdog(mockInventoryPersistence, mockCpsDataService, mockSyncUtils, mockDataSyncSemaphores)
+ def objectUnderTest = new DataSyncWatchdog(mockInventoryPersistence, mockCpsModuleService, mockCpsDataService, mockModuleOperationUtils, mockDataSyncSemaphores)
def compositeState = getCompositeState()
-
def yangModelCmHandle1 = createSampleYangModelCmHandle('cm-handle-1')
-
def yangModelCmHandle2 = createSampleYangModelCmHandle('cm-handle-2')
def 'Data Sync for Cm Handle State in READY and Operational Sync State in UNSYNCHRONIZED.'() {
given: 'sample resource data'
def resourceData = jsonString
and: 'sync utilities returns a cm handle twice'
- mockSyncUtils.getUnsynchronizedReadyCmHandles() >> [yangModelCmHandle1, yangModelCmHandle2]
+ mockModuleOperationUtils.getUnsynchronizedReadyCmHandles() >> [yangModelCmHandle1, yangModelCmHandle2]
+ and: 'we have the module and root nodes references to form the options field'
+ mockCpsModuleService.getRootNodeReferences(_, 'cm-handle-1') >> ['some-module-1:some-root-node']
+ mockCpsModuleService.getRootNodeReferences(_, 'cm-handle-2') >> ['some-module-2:some-root-node']
when: 'data sync poll is executed'
- objectUnderTest.executeUnSynchronizedReadyCmHandlePoll()
+ objectUnderTest.executeUnsynchronizedReadyCmHandleForInitialDataSync()
then: 'the inventory persistence cm handle returns a composite state for the first cm handle'
1 * mockInventoryPersistence.getCmHandleState('cm-handle-1') >> compositeState
and: 'the sync util returns first resource data'
- 1 * mockSyncUtils.getResourceData('cm-handle-1') >> resourceData
+ 1 * mockModuleOperationUtils.getResourceData('cm-handle-1', '(fields=some-module-1:some-root-node)') >> resourceData
and: 'the cm-handle data is saved'
1 * mockCpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cm-handle-1', jsonString, _)
and: 'the first cm handle operational sync state is updated'
then: 'the inventory persistence cm handle returns a composite state for the second cm handle'
1 * mockInventoryPersistence.getCmHandleState('cm-handle-2') >> compositeState
and: 'the sync util returns first resource data'
- 1 * mockSyncUtils.getResourceData('cm-handle-2') >> resourceData
+ 1 * mockModuleOperationUtils.getResourceData('cm-handle-2', '(fields=some-module-2:some-root-node)') >> resourceData
and: 'the cm-handle data is saved'
1 * mockCpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cm-handle-2', jsonString, _)
and: 'the second cm handle operational sync state is updated from "UNSYNCHRONIZED" to "SYNCHRONIZED"'
def 'Data Sync for Cm Handle State in READY and Operational Sync State in UNSYNCHRONIZED without resource data.'() {
given: 'sync utilities returns a cm handle'
- mockSyncUtils.getUnsynchronizedReadyCmHandles() >> [yangModelCmHandle1]
+ mockModuleOperationUtils.getUnsynchronizedReadyCmHandles() >> [yangModelCmHandle1]
+ and: 'the module service returns the module and root nodes references to form the options field'
+ mockCpsModuleService.getRootNodeReferences(_,'cm-handle-1') >> ['some-module-1:some-root-node']
when: 'data sync poll is executed'
- objectUnderTest.executeUnSynchronizedReadyCmHandlePoll()
+ objectUnderTest.executeUnsynchronizedReadyCmHandleForInitialDataSync()
then: 'the inventory persistence cm handle returns a composite state for the first cm handle'
1 * mockInventoryPersistence.getCmHandleState('cm-handle-1') >> compositeState
and: 'the sync util returns no resource data'
- 1 * mockSyncUtils.getResourceData('cm-handle-1') >> null
+ 1 * mockModuleOperationUtils.getResourceData('cm-handle-1', '(fields=some-module-1:some-root-node)') >> null
and: 'the cm-handle data is not saved'
0 * mockCpsDataService.saveData(*_)
}
def 'Data Sync for Cm Handle that is already being processed.'() {
given: 'sync utilities returns a cm handle'
- mockSyncUtils.getUnsynchronizedReadyCmHandles() >> [yangModelCmHandle1]
+ mockModuleOperationUtils.getUnsynchronizedReadyCmHandles() >> [yangModelCmHandle1]
+ and: 'the module service returns the module and root nodes references to form the options field'
+ mockCpsModuleService.getRootNodeReferences(_,'cm-handle-1') >> ['some-module-1:some-root-node']
and: 'the shared data sync semaphore indicate it is already being processed'
mockDataSyncSemaphores.putIfAbsent('cm-handle-1', _, _, _) >> 'something (not null)'
when: 'data sync poll is executed'
- objectUnderTest.executeUnSynchronizedReadyCmHandlePoll()
+ objectUnderTest.executeUnsynchronizedReadyCmHandleForInitialDataSync()
then: 'it is NOT processed e.g. state is not requested'
0 * mockInventoryPersistence.getCmHandleState(*_)
}
+ def 'Data sync handles exception during overall cm handle processing.'() {
+ given: 'sync utilities returns a cm handle'
+ mockModuleOperationUtils.getUnsynchronizedReadyCmHandles() >> [yangModelCmHandle1]
+ and: 'semaphore map allows processing'
+ mockDataSyncSemaphores.putIfAbsent('cm-handle-1', false, _, _) >> null
+ and: 'getting cm handle state throws exception'
+ mockInventoryPersistence.getCmHandleState('cm-handle-1') >> { throw new RuntimeException('some exception') }
+ when: 'data sync poll is executed'
+ objectUnderTest.executeUnsynchronizedReadyCmHandleForInitialDataSync()
+ then: 'no exception is thrown'
+ noExceptionThrown()
+ }
+
+ def 'Data sync handles exception during resource data retrieval.'() {
+ given: 'sync utilities returns a cm handle'
+ mockModuleOperationUtils.getUnsynchronizedReadyCmHandles() >> [yangModelCmHandle1]
+ and: 'semaphore map allows processing'
+ mockDataSyncSemaphores.putIfAbsent('cm-handle-1', false, _, _) >> null
+ and: 'module operations returns module and root nodes references'
+ mockCpsModuleService.getRootNodeReferences(_,'cm-handle-1') >> ['some-module-1:some-root-node', 'some-module-2:some-root-node']
+ when: 'data sync poll is executed'
+ objectUnderTest.executeUnsynchronizedReadyCmHandleForInitialDataSync()
+ then: 'cm handle state is retrieved'
+ 1 * mockInventoryPersistence.getCmHandleState('cm-handle-1') >> compositeState
+ and: 'first module sync succeeds'
+ 1 * mockModuleOperationUtils.getResourceData('cm-handle-1', '(fields=some-module-1:some-root-node)') >> jsonString
+ 1 * mockCpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cm-handle-1', jsonString, _)
+ and: 'second module sync throws exception'
+ 1 * mockModuleOperationUtils.getResourceData('cm-handle-1', '(fields=some-module-2:some-root-node)') >> { throw new RuntimeException('Some network error') }
+ and: 'no data is saved for failed module'
+ 0 * mockCpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cm-handle-1', _, _)
+ and: 'cm handle state is still updated (processing continues after module failure)'
+ 1 * mockInventoryPersistence.saveCmHandleState('cm-handle-1', compositeState)
+ 1 * mockDataSyncSemaphores.replace('cm-handle-1', true)
+ }
+
def createSampleYangModelCmHandle(cmHandleId) {
def compositeState = getCompositeState()
return new YangModelCmHandle(id: cmHandleId, compositeState: compositeState)
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022-2024 Nordix Foundation
+ * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved.
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
import ch.qos.logback.core.read.ListAppender
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.api.CpsModuleService
import org.onap.cps.ncmp.api.inventory.models.CompositeState
import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder
import org.onap.cps.ncmp.impl.data.DmiDataOperations
class ModuleOperationsUtilsSpec extends Specification{
def mockCmHandleQueries = Mock(CmHandleQueryService)
-
def mockDmiDataOperations = Mock(DmiDataOperations)
-
def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
def objectUnderTest = new ModuleOperationsUtils(mockCmHandleQueries, mockDmiDataOperations, jsonObjectMapper)
JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString)
and: 'DMI operations are mocked to return a response based on the scenario'
def responseEntity = new ResponseEntity<>(statusCode == HttpStatus.OK ? jsonNode : null, statusCode)
- mockDmiDataOperations.getAllResourceDataFromDmi('cm-handle-123', _) >> responseEntity
+ mockDmiDataOperations.getAllResourceDataFromDmi('cm-handle-123', _, 'some options') >> responseEntity
when: 'get resource data is called'
- def result = objectUnderTest.getResourceData('cm-handle-123')
+ def actualResult = objectUnderTest.getResourceData('cm-handle-123', 'some options')
then: 'the returned data matches the expected result'
- assert result == expectedResult
+ assert actualResult == expectedResult
where:
- scenario | statusCode | expectedResult
- 'successful response' | HttpStatus.OK | '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
- 'response with not found status' | HttpStatus.NOT_FOUND | null
- 'response with internal server error' | HttpStatus.INTERNAL_SERVER_ERROR | null
+ scenario | statusCode || expectedResult
+ 'successful response' | HttpStatus.OK || '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
+ 'response with not found status' | HttpStatus.NOT_FOUND || null
+ 'response with internal server error' | HttpStatus.INTERNAL_SERVER_ERROR || null
}
def 'Extract module set tag and number of attempt when lock reason contains #scenario'() {
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020-2025 Nordix Foundation
+ * Copyright (C) 2020-2025 OpenInfra Foundation Europe. All rights reserved.
* Modifications Copyright (C) 2020-2021 Pantheon.tech
* Modifications Copyright (C) 2022 TechMahindra Ltd.
* ================================================================================
*/
void deleteAllUnusedYangModuleData(String dataspaceName);
+ /**
+ * Get the collection of concatenated module-name:root-node of the provided anchor in the given dataspace.
+ *
+ * @param dataspaceName dataspace name
+ * @param anchorName anchor name
+ * @return collection of module and root nodes concatenated with : as separator
+ */
+ Collection<String> getRootNodeReferences(String dataspaceName, String anchorName);
+
}
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020-2025 Nordix Foundation
+ * Copyright (C) 2020-2025 OpenInfra Foundation Europe. All rights reserved.
* Modifications Copyright (C) 2020-2021 Pantheon.tech
* Modifications Copyright (C) 2022 Bell Canada
* Modifications Copyright (C) 2022 TechMahindra Ltd
import org.onap.cps.api.parameters.CascadeDeleteAllowed;
import org.onap.cps.spi.CpsModulePersistenceService;
import org.onap.cps.utils.CpsValidator;
+import org.onap.cps.utils.YangParser;
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder;
import org.onap.cps.yang.YangTextSchemaSourceSet;
import org.springframework.stereotype.Service;
private final CpsAnchorService cpsAnchorService;
private final CpsValidator cpsValidator;
private final TimedYangTextSchemaSourceSetBuilder timedYangTextSchemaSourceSetBuilder;
+ private final YangParser yangParser;
@Override
@Timed(value = "cps.module.service.schemaset.create",
cpsModulePersistenceService.deleteAllUnusedYangModuleData(dataspaceName);
}
+ @Override
+ public Collection<String> getRootNodeReferences(final String dataspaceName, final String anchorName) {
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
+ return yangParser.getRootNodeReferences(anchor);
+ }
+
private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) {
return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed;
}
import io.micrometer.core.annotation.Timed;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.exceptions.DataValidationException;
return convertToCpsPath(restConfStylePath, schemaContext);
}
+ /**
+ * Get the collection of concatenated module-name:root-node of the provided anchor.
+ *
+ * @param anchor Anchor
+ * @return Concatentated module and root node
+ */
+ public Set<String> getRootNodeReferences(final Anchor anchor) {
+ final SchemaContext schemaContext = getSchemaContext(anchor);
+ return schemaContext.getModules()
+ .stream()
+ .flatMap(module ->
+ module.getChildNodes()
+ .stream().map(rootNode ->
+ module.getName() + ":" + rootNode.getQName().getLocalName()))
+ .collect(Collectors.toSet());
+ }
+
private SchemaContext getSchemaContext(final Anchor anchor) {
return yangTextSchemaSourceSetCache.get(anchor.getDataspaceName(), anchor.getSchemaSetName())
.getSchemaContext();
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020-2025 Nordix Foundation
+ * Copyright (C) 2020-2025 OpenInfra Foundation Europe. All rights reserved.
* Modifications Copyright (C) 2020-2021 Pantheon.tech
* Modifications Copyright (C) 2020-2022 Bell Canada.
* Modifications Copyright (C) 2022 TechMahindra Ltd.
import org.onap.cps.api.model.ModuleDefinition
import org.onap.cps.api.model.ModuleReference
import org.onap.cps.api.model.SchemaSet
+import org.onap.cps.utils.YangParser
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder
import org.onap.cps.yang.YangTextSchemaSourceSet
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
def mockCpsAnchorService = Mock(CpsAnchorService)
def mockCpsValidator = Mock(CpsValidator)
def timedYangTextSchemaSourceSetBuilder = new TimedYangTextSchemaSourceSetBuilder()
+ def mockYangParser = Mock(YangParser)
- def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)
+ def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder,mockYangParser)
def 'Create schema set.'() {
when: 'Create schema set method is invoked'
1 * mockCpsModulePersistenceService.schemaSetExists('some-dataspace-name', 'some-schema-set-name')
}
+ def 'Get module and root nodes'() {
+ given: 'an anchor'
+ def myAnchor = createAnchors(1)[0]
+ mockCpsAnchorService.getAnchor('my-dataspace', 'my-anchor-1') >> myAnchor
+ when: 'module and root nodes are fetched for my anchor'
+ objectUnderTest.getRootNodeReferences('my-dataspace', 'my-anchor-1')
+ then: 'the call is delegated to the yang parser with correct anchor'
+ 1 * mockYangParser.getRootNodeReferences(myAnchor)
+ }
+
def getModuleReferences() {
return [new ModuleReference('some module name','some revision name')]
}
def mockCpsValidator = Mock(CpsValidator)
def timedYangTextSchemaSourceSetBuilder = new TimedYangTextSchemaSourceSetBuilder()
def yangParser = new YangParser(new YangParserHelper(), mockYangTextSchemaSourceSetCache, timedYangTextSchemaSourceSetBuilder)
- def cpsModuleServiceImpl = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)
+ def cpsModuleServiceImpl = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder,yangParser)
def mockCpsDataUpdateEventsProducer = Mock(CpsDataUpdateEventsProducer)
def dataNodeFactory = new DataNodeFactoryImpl(yangParser)
def cpsDataServiceImpl = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsDataUpdateEventsProducer, mockCpsAnchorService, dataNodeFactory, mockCpsValidator, yangParser)
import org.onap.cps.yang.YangTextSchemaSourceSet
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
+import org.opendaylight.yangtools.yang.model.api.Module
import org.opendaylight.yangtools.yang.model.api.SchemaContext
import spock.lang.Specification
1 * mockYangTextSchemaSourceSetCache.removeFromCache('my dataspace', 'my schema')
}
+ def 'Get module and root node references using an anchor'() {
+ given: 'a schema context with module and root node'
+ def mockModule = Mock(Module) {
+ getName() >> 'bookstore'
+ }
+ def mockRootNode = Mock(DataSchemaNode) {
+ getQName() >> QName.create('bookstore', 'book')
+ }
+ mockModule.getChildNodes() >> [mockRootNode]
+ mockSchemaContext.getModules() >> [mockModule]
+ when: 'we get module and root nodes references for the anchor'
+ def result = objectUnderTest.getRootNodeReferences(anchor)
+ then: 'the result contains expected module:rootnode combination'
+ assert result == ['bookstore:book'] as Set
+ }
+
}