diff options
8 files changed, 173 insertions, 23 deletions
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index 4f5a6a13e4..704721742f 100644 --- a/cps-ncmp-rest/docs/openapi/components.yaml +++ b/cps-ncmp-rest/docs/openapi/components.yaml @@ -40,6 +40,10 @@ components: type: array items: $ref: '#/components/schemas/RestCmHandle' + updatedCmHandles: + type: array + items: + $ref: '#/components/schemas/RestCmHandle' RestCmHandle: required: 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 84dcc770d9..0b9cb5ad59 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 @@ -24,9 +24,9 @@ package org.onap.cps.ncmp.api.impl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; @@ -124,24 +124,48 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService @Override public void updateDmiPluginRegistration(final DmiPluginRegistration dmiPluginRegistration) { + if (dmiPluginRegistration.getCreatedCmHandles() != null) { + parseAndCreateCmHandlesInDmiRegistration(dmiPluginRegistration); + } + if (dmiPluginRegistration.getUpdatedCmHandles() != null) { + parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration); + } + } + + private void parseAndCreateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) { try { - final List<PersistenceCmHandle> persistenceCmHandles = - new ArrayList<>(); + final List<PersistenceCmHandle> createdPersistenceCmHandles = + new LinkedList<>(); for (final CmHandle cmHandle: dmiPluginRegistration.getCreatedCmHandles()) { - final var persistenceCmHandle = new PersistenceCmHandle(); - persistenceCmHandle.setDmiServiceName(dmiPluginRegistration.getDmiPlugin()); - persistenceCmHandle.setId(cmHandle.getCmHandleID()); - persistenceCmHandle.setAdditionalProperties(cmHandle.getCmHandleProperties()); - persistenceCmHandles.add(persistenceCmHandle); + createdPersistenceCmHandles.add(toPersistenceCmHandle(dmiPluginRegistration, cmHandle)); } - final var persistenceCmHandlesList = new PersistenceCmHandlesList(); - persistenceCmHandlesList.setCmHandles(persistenceCmHandles); + final PersistenceCmHandlesList persistenceCmHandlesList = new PersistenceCmHandlesList(); + persistenceCmHandlesList.setCmHandles(createdPersistenceCmHandles); final String cmHandleJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList); - cpsDataService.saveListNodeData(NCMP_DATASPACE_NAME, - NCMP_DMI_REGISTRY_ANCHOR, - "/dmi-registry", + cpsDataService.saveListNodeData(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry", cmHandleJsonData); } catch (final JsonProcessingException e) { + log.error("Parsing error occurred while converting Object to JSON for Dmi Registry."); + throw new DataValidationException( + "Parsing error occurred while processing DMI Plugin Registration" + dmiPluginRegistration, e + .getMessage(), e); + } + } + + private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) { + try { + final List<PersistenceCmHandle> updatedPersistenceCmHandles = + new LinkedList<>(); + for (final CmHandle cmHandle: dmiPluginRegistration.getUpdatedCmHandles()) { + updatedPersistenceCmHandles.add(toPersistenceCmHandle(dmiPluginRegistration, cmHandle)); + } + final PersistenceCmHandlesList persistenceCmHandlesList = new PersistenceCmHandlesList(); + persistenceCmHandlesList.setCmHandles(updatedPersistenceCmHandles); + final String cmHandlesJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList); + cpsDataService.updateNodeLeavesAndExistingDescendantLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + "/dmi-registry", cmHandlesJsonData); + } catch (final JsonProcessingException e) { + log.error("Parsing error occurred while converting Object to JSON Dmi Registry."); throw new DataValidationException( "Parsing error occurred while processing DMI Plugin Registration" + dmiPluginRegistration, e .getMessage(), e); @@ -216,5 +240,13 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } + private PersistenceCmHandle toPersistenceCmHandle(final DmiPluginRegistration dmiPluginRegistration, + final CmHandle cmHandle) { + final PersistenceCmHandle persistenceCmHandle = new PersistenceCmHandle(); + persistenceCmHandle.setDmiServiceName(dmiPluginRegistration.getDmiPlugin()); + persistenceCmHandle.setId(cmHandle.getCmHandleID()); + persistenceCmHandle.setAdditionalProperties(cmHandle.getCmHandleProperties()); + return persistenceCmHandle; + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java index 4017c4a56e..fcf9e92d03 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java @@ -21,19 +21,26 @@ package org.onap.cps.ncmp.api.models; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import java.util.List; import lombok.Getter; import lombok.Setter; /** - * DmiRegistry. + * Dmi Registry request object. */ @Getter @Setter +@JsonInclude(Include.NON_NULL) public class DmiPluginRegistration { private String dmiPlugin; private List<CmHandle> createdCmHandles; + private List<CmHandle> updatedCmHandles; + + private List<CmHandle> deletedCmHandles; + } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 65d96a4296..6b32a2dc7a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -33,16 +33,21 @@ import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.DataNode import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import spock.lang.Shared import spock.lang.Specification class NetworkCmProxyDataServiceImplSpec extends Specification { + @Shared + def persistenceCmHandle = new CmHandle() + def mockCpsDataService = Mock(CpsDataService) def mockCpsQueryService = Mock(CpsQueryService) def mockDmiOperations = Mock(DmiOperations) def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsDataService, mockCpsQueryService, new ObjectMapper()) def cmHandle = 'some handle' + def expectedDataspaceName = 'NFP-Operational' def 'Query data nodes by cps path with #fetchDescendantsOption.'() { given: 'a cm Handle and a cps path' @@ -102,19 +107,29 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { then: 'the persistence service is called once with the correct parameters' 1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData) } - def 'Register CM Handle Event.'() { + + def 'Register or re-register a DMI Plugin with #scenario cm handles.'() { given: 'a registration ' + def dmiRegistryAnchor = 'ncmp-dmi-registry' def dmiPluginRegistration = new DmiPluginRegistration() dmiPluginRegistration.dmiPlugin = 'my-server' - def cmHandle = new CmHandle() - cmHandle.cmHandleID = '123' - cmHandle.cmHandleProperties = [ name1: 'value1', name2: 'value2'] - dmiPluginRegistration.createdCmHandles = [ cmHandle ] + persistenceCmHandle.cmHandleID = '123' + persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2'] + dmiPluginRegistration.createdCmHandles = createdCmHandles + dmiPluginRegistration.updatedCmHandles = updatedCmHandles def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}' when: 'registration is updated' objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration) - then: 'the CPS service method is invoked once with the expected parameters' - 1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData) + then: 'the CPS save list node data is invoked with the expected parameters' + expectedCallsToSaveNode * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData) + and: 'update Node and Child Data Nodes is invoked with correct parameter' + expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin', dmiRegistryAnchor, '/dmi-registry', expectedJsonData) + where: + scenario | createdCmHandles | updatedCmHandles || expectedCallsToSaveNode | expectedCallsToUpdateNode + 'create' | [persistenceCmHandle ] | [] || 1 | 0 + 'update' | [] | [persistenceCmHandle ] || 0 | 1 + 'create and update' | [persistenceCmHandle ] | [persistenceCmHandle ] || 1 | 1 + } def 'Get resource data for pass-through operational from dmi.'() { given: 'xpath' diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java index 6036f92225..2583e9905b 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java @@ -118,4 +118,17 @@ public interface CpsDataService { * @param listNodeXpath list node xpath */ void deleteListNodeData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String listNodeXpath); + + /** + * Updates leaves of DataNode for given dataspace and anchor using xpath, + * along with the leaves of each Child Data Node which already exists. + * This method will throw an exception if data node update or any descendant update does not exist. + * + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @param parentNodeXpath xpath + * @param dataNodeUpdatesAsJson json data representing data node updates + */ + void updateNodeLeavesAndExistingDescendantLeaves(String dataspaceName, String anchorName, String parentNodeXpath, + String dataNodeUpdatesAsJson); } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java index 5e6e1a2687..b45645bc42 100755 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java @@ -98,6 +98,18 @@ public class CpsDataServiceImpl implements CpsDataService { } @Override + public void updateNodeLeavesAndExistingDescendantLeaves(final String dataspaceName, final String anchorName, + final String parentNodeXpath, + final String dataNodeUpdatesAsJson) { + final Collection<DataNode> dataNodeUpdates = + buildDataNodeCollectionFromJson(dataspaceName, anchorName, parentNodeXpath, dataNodeUpdatesAsJson); + for (final DataNode dataNodeUpdate : dataNodeUpdates) { + processDataNodeUpdate(dataspaceName, anchorName, dataNodeUpdate); + } + notificationService.processDataUpdatedEvent(dataspaceName, anchorName); + } + + @Override public void replaceNodeTree(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData) { final var dataNode = buildDataNodeFromJson(dataspaceName, anchorName, parentNodeXpath, jsonData); @@ -160,4 +172,17 @@ public class CpsDataServiceImpl implements CpsDataService { private SchemaContext getSchemaContext(final String dataspaceName, final String schemaSetName) { return yangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName).getSchemaContext(); } + + private void processDataNodeUpdate(final String dataspaceName, final String anchorName, + final DataNode dataNodeUpdate) { + if (dataNodeUpdate == null) { + return; + } + cpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, dataNodeUpdate.getXpath(), + dataNodeUpdate.getLeaves()); + final Collection<DataNode> childDataNodeUpdates = dataNodeUpdate.getChildDataNodes(); + for (final DataNode childDataNodeUpdate : childDataNodeUpdates) { + processDataNodeUpdate(dataspaceName, anchorName, childDataNodeUpdate); + } + } } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy index 122039728a..97eac5aaa9 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy @@ -28,7 +28,6 @@ import org.onap.cps.api.CpsModuleService import org.onap.cps.notification.NotificationService import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.FetchDescendantsOption -import org.onap.cps.spi.exceptions.CpsPathException import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.Anchor import org.onap.cps.spi.model.DataNodeBuilder @@ -148,10 +147,23 @@ class CpsDataServiceImplSpec extends Specification { thrown(DataValidationException) where: 'following parameters were used' scenario | jsonData - 'multiple leaves' | '{"code": "01","name": "some-name"}' + 'multiple expectedLeaves' | '{"code": "01","name": "some-name"}' 'one leaf' | '{"name": "some-name"}' } + def 'Update cm-handle properties' () { + given: 'a dmi registry model' + setupSchemaSetMocks('dmi-registry.yang') + and: 'the expected json string' + def jsonData = '{"cm-handles":[{"id":"cmHandle001", "additional-properties":[{"name":"P1"}]}]}' + when: 'update data method is invoked with json data and parent node xpath' + objectUnderTest.updateNodeLeavesAndExistingDescendantLeaves(dataspaceName, anchorName, '/dmi-registry', jsonData) + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, "/dmi-registry/cm-handles[@id='cmHandle001']", ['id': 'cmHandle001']) + and: 'the data updated event is sent to the notification service' + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName) + } + def 'Replace data node: #scenario.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') diff --git a/cps-service/src/test/resources/dmi-registry.yang b/cps-service/src/test/resources/dmi-registry.yang new file mode 100644 index 0000000000..3c2d893b2a --- /dev/null +++ b/cps-service/src/test/resources/dmi-registry.yang @@ -0,0 +1,42 @@ +module dmi-registry { + + yang-version 1.1; + + namespace "org:onap:cps:ncmp"; + + prefix dmi-reg; + + revision "2021-05-20" { + description + "Initial Version"; + } + + container dmi-registry { + + list cm-handles { + + key "id"; + + leaf id { + type string; + } + + leaf dmi-service-name { + type string; + } + + list additional-properties { + + key "name"; + + leaf name { + type string; + } + + leaf value { + type string; + } + } + } + } +}
\ No newline at end of file |