diff options
14 files changed, 247 insertions, 78 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index acf78036ad..39309d842b 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -71,7 +71,7 @@ spring: notification:
data-updated:
enabled: false
- topic: ${CPS_CHANGE_EVENT_TOPIC:cps.cfg-state-events}
+ topic: ${CPS_CHANGE_EVENT_TOPIC:cps.data-updated-events}
filters:
enabled-dataspaces: ${NOTIFICATION_DATASPACE_FILTER_PATTERNS:""}
async-executor:
diff --git a/cps-events/src/main/resources/schemas/cps-data-updated-event-schema.json b/cps-events/src/main/resources/schemas/cps-data-updated-event-schema.json index de445ec722..95dc605da4 100644 --- a/cps-events/src/main/resources/schemas/cps-data-updated-event-schema.json +++ b/cps-events/src/main/resources/schemas/cps-data-updated-event-schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "urn:cps:org.onap.cps:data-updated-event-schema:1.1.0-SNAPSHOT", + "$id": "urn:cps:org.onap.cps:data-updated-event-schema:v1", "$ref": "#/definitions/CpsDataUpdatedEvent", @@ -12,10 +12,9 @@ "type": "object", "properties": { "schema": { - "description": "The schema, including its version, that this event adheres to.", + "description": "The schema, including its version, that this event adheres to. E.g. 'urn:cps:org.onap.cps:data-updated-event-schema:v99'.", "type": "string", - "default": "urn:cps:org.onap.cps:data-updated-event-schema:1.1.0-SNAPSHOT", - "enum": ["urn:cps:org.onap.cps:data-updated-event-schema:1.1.0-SNAPSHOT"] + "format": "uri" }, "id": { "description": "The unique id identifying the event for the specified source. Producer must ensure that source + id is unique for each distinct event.", @@ -69,7 +68,7 @@ } }, "required": [ - "timestamp", + "observedTimestamp", "dataspaceName", "schemaSetName", "anchorName", @@ -85,4 +84,4 @@ } -}
\ No newline at end of file +} diff --git a/cps-events/src/test/groovy/org/onap/cps/event/CpsDataUpdatedEventSpec.groovy b/cps-events/src/test/groovy/org/onap/cps/event/CpsDataUpdatedEventSpec.groovy index f72eceed1c..545b183378 100644 --- a/cps-events/src/test/groovy/org/onap/cps/event/CpsDataUpdatedEventSpec.groovy +++ b/cps-events/src/test/groovy/org/onap/cps/event/CpsDataUpdatedEventSpec.groovy @@ -33,13 +33,13 @@ class CpsDataUpdatedEventSpec extends Specification { def objectMapper = new ObjectMapper() final DATASPACE_NAME = 'my-dataspace' - final BOOKSTORE_SCHEMA_SET = 'bootstore-schemaset' + final BOOKSTORE_SCHEMA_SET = 'bookstore-schemaset' final ANCHOR_NAME = 'chapters' final EVENT_TIMESTAMP = '2020-12-01T00:00:00.000+0000' final EVENT_ID = '77b8f114-4562-4069-8234-6d059ff742ac' final EVENT_SOURCE = new URI('urn:cps:org.onap.cps') final EVENT_TYPE = 'org.onap.cps.data-updated-event' - final EVENT_SCHEMA = 'urn:cps:org.onap.cps:data-updated-event-schema:1.1.0-SNAPSHOT' + final EVENT_SCHEMA = new URI('urn:cps:org.onap.cps:data-updated-event-schema:v1') final DATA = [ 'test:bookstore': [ @@ -67,7 +67,7 @@ class CpsDataUpdatedEventSpec extends Specification { then: 'CpsDataUpdatedEvent POJO has the excepted values' cpsDataUpdatedEvent.id == EVENT_ID cpsDataUpdatedEvent.source == EVENT_SOURCE - cpsDataUpdatedEvent.schema.value() == EVENT_SCHEMA + cpsDataUpdatedEvent.schema == EVENT_SCHEMA cpsDataUpdatedEvent.type == EVENT_TYPE def content = cpsDataUpdatedEvent.content content.observedTimestamp == EVENT_TIMESTAMP @@ -90,8 +90,7 @@ class CpsDataUpdatedEventSpec extends Specification { and: 'CpsDataUpdatedEvent with the content' def cpsDataUpdateEvent = new CpsDataUpdatedEvent() cpsDataUpdateEvent - .withSchema( - CpsDataUpdatedEvent.Schema.fromValue(EVENT_SCHEMA)) + .withSchema(EVENT_SCHEMA) .withId(EVENT_ID) .withSource(EVENT_SOURCE) .withType(EVENT_TYPE) @@ -111,4 +110,4 @@ class CpsDataUpdatedEventSpec extends Specification { ) } -}
\ No newline at end of file +} diff --git a/cps-events/src/test/resources/bookstore-chapters.json b/cps-events/src/test/resources/bookstore-chapters.json index de46b71841..753426a60a 100644 --- a/cps-events/src/test/resources/bookstore-chapters.json +++ b/cps-events/src/test/resources/bookstore-chapters.json @@ -1,12 +1,12 @@ { - "schema": "urn:cps:org.onap.cps:data-updated-event-schema:1.1.0-SNAPSHOT", + "schema": "urn:cps:org.onap.cps:data-updated-event-schema:v1", "id": "77b8f114-4562-4069-8234-6d059ff742ac", "source": "urn:cps:org.onap.cps", "type": "org.onap.cps.data-updated-event", "content": { "observedTimestamp": "2020-12-01T00:00:00.000+0000", "dataspaceName": "my-dataspace", - "schemaSetName": "bootstore-schemaset", + "schemaSetName": "bookstore-schemaset", "anchorName": "chapters", "data": { "test:bookstore":{ @@ -31,4 +31,4 @@ } } } -}
\ No newline at end of file +} 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 c2be9e9db5..a8c92d11f5 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,10 @@ 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.Collections; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.validation.constraints.NotNull; @@ -70,10 +71,11 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService /** * Constructor Injection for Dependencies. - * @param dmiOperations dmi operation - * @param cpsDataService Data Service Interface + * + * @param dmiOperations dmi operation + * @param cpsDataService Data Service Interface * @param cpsQueryService Query Service Interface - * @param objectMapper Object Mapper + * @param objectMapper Object Mapper */ public NetworkCmProxyDataServiceImpl(final DmiOperations dmiOperations, final CpsDataService cpsDataService, final CpsQueryService cpsQueryService, final ObjectMapper objectMapper) { @@ -95,7 +97,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService @Override public Collection<DataNode> queryDataNodes(final String cmHandle, final String cpsPath, - final FetchDescendantsOption fetchDescendantsOption) { + final FetchDescendantsOption fetchDescendantsOption) { return cpsQueryService.queryDataNodes(getDataspaceName(), cmHandle, cpsPath, fetchDescendantsOption); } @@ -125,24 +127,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<>(); - 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); + final List<PersistenceCmHandle> createdPersistenceCmHandles = + new LinkedList<>(); + for (final CmHandle cmHandle : dmiPluginRegistration.getCreatedCmHandles()) { + 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); @@ -151,56 +177,56 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService @Override public Object getResourceDataOperationalForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, - final String acceptParam, - final String fieldsQueryParam, - final Integer depthQueryParam) { + final @NotNull String resourceIdentifier, + final String acceptParam, + final String fieldsQueryParam, + final Integer depthQueryParam) { final var dataNode = fetchDataNodeFromDmiRegistryForCmHandle(cmHandle); final var dmiServiceName = String.valueOf(dataNode.getLeaves().get("dmi-service-name")); final Collection<DataNode> additionalPropsList = dataNode.getChildDataNodes(); final var jsonBody = prepareOperationBody(GenericRequestBody.OperationEnum.READ, additionalPropsList); final ResponseEntity<Object> response = dmiOperations.getResouceDataOperationalFromDmi(dmiServiceName, - cmHandle, - resourceIdentifier, - fieldsQueryParam, - depthQueryParam, - acceptParam, - jsonBody); + cmHandle, + resourceIdentifier, + fieldsQueryParam, + depthQueryParam, + acceptParam, + jsonBody); return handleResponse(response); } @Override public Object getResourceDataPassThroughRunningForCmHandle(final @NotNull String cmHandle, - final @NotNull String resourceIdentifier, - final String accept, - final String fields, - final Integer depth) { + final @NotNull String resourceIdentifier, + final String accept, + final String fields, + final Integer depth) { final var cmHandleDataNode = fetchDataNodeFromDmiRegistryForCmHandle(cmHandle); final var dmiServiceName = String.valueOf(cmHandleDataNode.getLeaves().get("dmi-service-name")); final Collection<DataNode> additionalPropsList = cmHandleDataNode.getChildDataNodes(); final var dmiRequesBody = prepareOperationBody(GenericRequestBody.OperationEnum.READ, additionalPropsList); final ResponseEntity<Object> response = dmiOperations.getResouceDataPassThroughRunningFromDmi(dmiServiceName, - cmHandle, - resourceIdentifier, - fields, - depth, - accept, - dmiRequesBody); + cmHandle, + resourceIdentifier, + fields, + depth, + accept, + dmiRequesBody); return handleResponse(response); } private DataNode fetchDataNodeFromDmiRegistryForCmHandle(final String cmHandle) { final String xpathForDmiRegistryToFetchCmHandle = "/dmi-registry/cm-handles[@id='" + cmHandle + "']"; final var dataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, - NCMP_DMI_REGISTRY_ANCHOR, - xpathForDmiRegistryToFetchCmHandle, - FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); + NCMP_DMI_REGISTRY_ANCHOR, + xpathForDmiRegistryToFetchCmHandle, + FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); return dataNode; } private String prepareOperationBody(final GenericRequestBody.OperationEnum operation, - final Collection<DataNode> additionalPropertyList) { + final Collection<DataNode> additionalPropertyList) { final var requestBody = new GenericRequestBody(); final Map<String, String> additionalPropertyMap = getAdditionalPropertiesMap(additionalPropertyList); requestBody.setOperation(GenericRequestBody.OperationEnum.READ); @@ -211,7 +237,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } catch (final JsonProcessingException je) { log.error("Parsing error occurred while converting Object to JSON."); throw new NcmpException("Parsing error occurred while converting given object to JSON.", - je.getMessage()); + je.getMessage()); } } @@ -220,9 +246,9 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return null; } final Map<String, String> additionalPropertyMap = new LinkedHashMap<>(); - for (final var node: additionalPropertyList) { + for (final var node : additionalPropertyList) { additionalPropertyMap.put(String.valueOf(node.getLeaves().get("name")), - String.valueOf(node.getLeaves().get("value"))); + String.valueOf(node.getLeaves().get("value"))); } return additionalPropertyMap; } @@ -232,10 +258,22 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return responseEntity.getBody(); } else { throw new NcmpException("Not able to get resource data.", - "DMI status code: " + responseEntity.getStatusCodeValue() - + ", DMI response body: " + responseEntity.getBody()); + "DMI status code: " + responseEntity.getStatusCodeValue() + + ", DMI response body: " + responseEntity.getBody()); } } + private PersistenceCmHandle toPersistenceCmHandle(final DmiPluginRegistration dmiPluginRegistration, + final CmHandle cmHandle) { + final PersistenceCmHandle persistenceCmHandle = new PersistenceCmHandle(); + persistenceCmHandle.setDmiServiceName(dmiPluginRegistration.getDmiPlugin()); + persistenceCmHandle.setId(cmHandle.getCmHandleID()); + if (cmHandle.getCmHandleProperties() == null) { + persistenceCmHandle.setAdditionalProperties(Collections.EMPTY_MAP); + } else { + 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 ac290af411..34c9541759 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,20 +107,45 @@ 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' + 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 'Register a DMI Plugin for the given cmHandle without additional properties.'() { + given: 'a registration without cmHandle properties ' + def dmiPluginRegistration = new DmiPluginRegistration() + dmiPluginRegistration.dmiPlugin = 'my-server' + persistenceCmHandle.cmHandleID = '123' + persistenceCmHandle.cmHandleProperties = null + dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ] + def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}' + when: 'registration is updated' + objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration) + then: 'the CPS save list node data is invoked with the expected parameters' 1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData) } + def 'Get resource data for pass-through operational from dmi.'() { given: 'xpath' def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']" 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 a512f67baf..8989dc80ef 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 @@ -100,6 +100,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); @@ -170,4 +182,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/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java b/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java index 6b7f5a89be..e0c8fe7055 100644 --- a/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java +++ b/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java @@ -29,7 +29,6 @@ import org.onap.cps.api.CpsAdminService; import org.onap.cps.api.CpsDataService; import org.onap.cps.event.model.Content; import org.onap.cps.event.model.CpsDataUpdatedEvent; -import org.onap.cps.event.model.CpsDataUpdatedEvent.Schema; import org.onap.cps.event.model.Data; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.Anchor; @@ -40,6 +39,7 @@ import org.springframework.stereotype.Component; @Component public class CpsDataUpdatedEventFactory { + private static final URI EVENT_SCHEMA; private static final URI EVENT_SOURCE; private static final String EVENT_TYPE = "org.onap.cps.data-updated-event"; private static final DateTimeFormatter dateTimeFormatter = @@ -47,6 +47,7 @@ public class CpsDataUpdatedEventFactory { static { try { + EVENT_SCHEMA = new URI("urn:cps:org.onap.cps:data-updated-event-schema:v1"); EVENT_SOURCE = new URI("urn:cps:org.onap.cps"); } catch (final URISyntaxException e) { // As it is fixed string, I don't expect to see this error @@ -54,8 +55,8 @@ public class CpsDataUpdatedEventFactory { } } - private CpsDataService cpsDataService; - private CpsAdminService cpsAdminService; + private final CpsDataService cpsDataService; + private final CpsAdminService cpsAdminService; public CpsDataUpdatedEventFactory(final CpsDataService cpsDataService, final CpsAdminService cpsAdminService) { this.cpsDataService = cpsDataService; @@ -80,7 +81,7 @@ public class CpsDataUpdatedEventFactory { final var cpsDataUpdatedEvent = new CpsDataUpdatedEvent(); cpsDataUpdatedEvent.withContent(createContent(anchor, dataNode)); cpsDataUpdatedEvent.withId(UUID.randomUUID().toString()); - cpsDataUpdatedEvent.withSchema(Schema.URN_CPS_ORG_ONAP_CPS_DATA_UPDATED_EVENT_SCHEMA_1_1_0_SNAPSHOT); + cpsDataUpdatedEvent.withSchema(EVENT_SCHEMA); cpsDataUpdatedEvent.withSource(EVENT_SOURCE); cpsDataUpdatedEvent.withType(EVENT_TYPE); return cpsDataUpdatedEvent; 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/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy index aecc3f7ee0..2ce77bd1a8 100644 --- a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy @@ -21,7 +21,6 @@ package org.onap.cps.notification import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsDataService -import org.onap.cps.event.model.CpsDataUpdatedEvent import org.onap.cps.event.model.Data import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.Anchor @@ -61,7 +60,7 @@ class CpsDataUpdateEventFactorySpec extends Specification { with(cpsDataUpdatedEvent) { type == 'org.onap.cps.data-updated-event' source == new URI('urn:cps:org.onap.cps') - schema == CpsDataUpdatedEvent.Schema.URN_CPS_ORG_ONAP_CPS_DATA_UPDATED_EVENT_SCHEMA_1_1_0_SNAPSHOT + schema == new URI('urn:cps:org.onap.cps:data-updated-event-schema:v1') StringUtils.hasText(id) content != null } 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 |