diff options
author | Rudrangi Anupriya <ra00745022@techmahindra.com> | 2024-07-11 21:56:24 +0530 |
---|---|---|
committer | Rudrangi Anupriya <ra00745022@techmahindra.com> | 2024-07-16 11:00:11 +0000 |
commit | 760dd950e6f6fcb6f49c6f9d92a33f538adffd24 (patch) | |
tree | cccab533a2837a60e527ff1e75d6dbcd01197d6a | |
parent | ac58e919008c4449b389d3681a6f8aa216cb5f8f (diff) |
XML content support on replace a node with descendants
Issue-ID: CPS-2282
Change-Id: Ibb7ffb65ccbb03703266712c6d5c2eade0e7ab4b
Signed-off-by: Rudrangi Anupriya <ra00745022@techmahindra.com>
11 files changed, 112 insertions, 60 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java index 2930d4f87c..cd1237b884 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2022-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2023 TechMahindra Ltd. + * Modifications Copyright (C) 2024 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,7 @@ import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleDefinition; import org.onap.cps.spi.model.ModuleReference; import org.onap.cps.spi.utils.CpsValidator; +import org.onap.cps.utils.ContentType; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Component; @@ -95,7 +96,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv public void saveCmHandleState(final String cmHandleId, final CompositeState compositeState) { final String cmHandleJsonData = createStateJsonData(jsonObjectMapper.asJsonString(compositeState)); cpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now()); + getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now(), ContentType.JSON); } @Override @@ -105,7 +106,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv getXPathForCmHandleById(cmHandleId), createStateJsonData(jsonObjectMapper.asJsonString(compositeState)))); cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - cmHandlesJsonDataMap, OffsetDateTime.now()); + cmHandlesJsonDataMap, OffsetDateTime.now(), ContentType.JSON); } @Override diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy index d58a1dfa06..e098fb81d7 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2022-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2023 TechMahindra Ltd. + * Modifications Copyright (C) 2024 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.ModuleDefinition import org.onap.cps.spi.model.ModuleReference import org.onap.cps.spi.utils.CpsValidator +import org.onap.cps.utils.ContentType import org.onap.cps.utils.JsonObjectMapper import spock.lang.Shared import spock.lang.Specification @@ -166,7 +167,7 @@ class InventoryPersistenceImplSpec extends Specification { when: 'update cm handle state is invoked with the #scenario state' objectUnderTest.saveCmHandleState(cmHandleId, compositeState) then: 'update node leaves is invoked with the correct params' - 1 * mockCpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']', expectedJsonData, _ as OffsetDateTime) + 1 * mockCpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']', expectedJsonData, _ as OffsetDateTime, ContentType.JSON) where: 'the following states are used' scenario | cmHandleState || expectedJsonData 'READY' | CmHandleState.READY || '{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}' @@ -182,7 +183,7 @@ class InventoryPersistenceImplSpec extends Specification { def cmHandleStateMap = ['Some-Cm-Handle1' : compositeState1, 'Some-Cm-Handle2' : compositeState2] objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap) then: 'update node leaves is invoked with the correct params' - 1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, _ as OffsetDateTime) + 1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, _ as OffsetDateTime, ContentType.JSON) where: 'the following states are used' scenario | cmHandleState || cmHandlesJsonDataMap 'READY' | CmHandleState.READY || ['/dmi-registry/cm-handles[@id=\'Some-Cm-Handle1\']':'{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle2\']':'{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}'] diff --git a/cps-rest/docs/openapi/components.yml b/cps-rest/docs/openapi/components.yml index 01375f503d..99502e3083 100644 --- a/cps-rest/docs/openapi/components.yml +++ b/cps-rest/docs/openapi/components.yml @@ -279,10 +279,10 @@ components: type: string enum: [v1, v2] default: v2 - contentTypeHeader: + contentTypeInHeader: name: Content-Type in: header - description: Content type header + description: Content type in header schema: type: string example: 'application/json' diff --git a/cps-rest/docs/openapi/cpsData.yml b/cps-rest/docs/openapi/cpsData.yml index d13c285817..80d07c806b 100644 --- a/cps-rest/docs/openapi/cpsData.yml +++ b/cps-rest/docs/openapi/cpsData.yml @@ -94,7 +94,7 @@ nodesByDataspaceAndAnchor: - $ref: 'components.yml#/components/parameters/anchorNameInPath' - $ref: 'components.yml#/components/parameters/xpathInQuery' - $ref: 'components.yml#/components/parameters/observedTimestampInQuery' - - $ref: 'components.yml#/components/parameters/contentTypeHeader' + - $ref: 'components.yml#/components/parameters/contentTypeInHeader' requestBody: required: true content: @@ -137,7 +137,7 @@ nodesByDataspaceAndAnchor: - $ref: 'components.yml#/components/parameters/anchorNameInPath' - $ref: 'components.yml#/components/parameters/xpathInQuery' - $ref: 'components.yml#/components/parameters/observedTimestampInQuery' - - $ref: 'components.yml#/components/parameters/contentTypeHeader' + - $ref: 'components.yml#/components/parameters/contentTypeInHeader' requestBody: required: true content: @@ -197,15 +197,24 @@ nodesByDataspaceAndAnchor: - $ref: 'components.yml#/components/parameters/anchorNameInPath' - $ref: 'components.yml#/components/parameters/xpathInQuery' - $ref: 'components.yml#/components/parameters/observedTimestampInQuery' + - $ref: 'components.yml#/components/parameters/contentTypeInHeader' requestBody: required: true content: application/json: schema: - type: object + type: string examples: dataSample: $ref: 'components.yml#/components/examples/dataSample' + application/xml: + schema: + type: object + xml: + name: stores + examples: + dataSample: + $ref: 'components.yml#/components/examples/dataSampleXml' responses: '200': $ref: 'components.yml#/components/responses/Ok' diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java index f579c82d25..6100b7edd9 100755 --- a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java @@ -49,7 +49,6 @@ import org.onap.cps.utils.PrefixResolver; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @@ -70,11 +69,10 @@ public class DataRestController implements CpsDataApi { @Override public ResponseEntity<String> createNode(final String apiVersion, final String dataspaceName, final String anchorName, - @RequestHeader(value = "Content-Type") final String contentTypeHeader, + final String contentTypeInHeader, final String nodeData, final String parentNodeXpath, final String observedTimestamp) { - final ContentType contentType = contentTypeHeader.contains(MediaType.APPLICATION_XML_VALUE) ? ContentType.XML - : ContentType.JSON; + final ContentType contentType = getContentTypeFromHeader(contentTypeInHeader); if (isRootXpath(parentNodeXpath)) { cpsDataService.saveData(dataspaceName, anchorName, nodeData, toOffsetDateTime(observedTimestamp), contentType); @@ -137,23 +135,23 @@ public class DataRestController implements CpsDataApi { @Override public ResponseEntity<Object> updateNodeLeaves(final String apiVersion, final String dataspaceName, - final String anchorName, final String contentTypeHeader, + final String anchorName, final String contentTypeInHeader, final String nodeData, final String parentNodeXpath, final String observedTimestamp) { - final ContentType contentType = contentTypeHeader.contains(MediaType.APPLICATION_XML_VALUE) ? ContentType.XML - : ContentType.JSON; + final ContentType contentType = getContentTypeFromHeader(contentTypeInHeader); cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, nodeData, toOffsetDateTime(observedTimestamp), contentType); return new ResponseEntity<>(HttpStatus.OK); } @Override - public ResponseEntity<Object> replaceNode(final String apiVersion, - final String dataspaceName, final String anchorName, - final Object jsonData, final String parentNodeXpath, final String observedTimestamp) { - cpsDataService - .updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, - jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp)); + public ResponseEntity<Object> replaceNode(final String apiVersion, final String dataspaceName, + final String anchorName, final String contentTypeInHeader, + final String nodeData, final String parentNodeXpath, + final String observedTimestamp) { + final ContentType contentType = getContentTypeFromHeader(contentTypeInHeader); + cpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, + nodeData, toOffsetDateTime(observedTimestamp), contentType); return new ResponseEntity<>(HttpStatus.OK); } @@ -213,6 +211,10 @@ public class DataRestController implements CpsDataApi { return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaBetweenAnchors), HttpStatus.OK); } + private static ContentType getContentTypeFromHeader(final String contentTypeInHeader) { + return contentTypeInHeader.contains(MediaType.APPLICATION_XML_VALUE) ? ContentType.XML : ContentType.JSON; + } + private static boolean isRootXpath(final String xpath) { return ROOT_XPATH.equals(xpath); } diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy index 317b9c5b7c..205d85dc26 100755 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy @@ -456,19 +456,22 @@ class DataRestControllerSpec extends Specification { def response = mvc.perform( put(endpoint) - .contentType(MediaType.APPLICATION_JSON) - .content(requestBodyJson) + .contentType(contentType) + .content(requestBody) .param('xpath', inputXpath)) .andReturn().response then: 'the service method is invoked with expected parameters' - 1 * mockCpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, xpathServiceParameter, expectedJsonData, noTimestamp) + 1 * mockCpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, xpathServiceParameter, expectedData, noTimestamp, expectedContentType) and: 'response status indicates success' response.status == HttpStatus.OK.value() where: - scenario | inputXpath || xpathServiceParameter - 'root node by default' | '' || '/' - 'root node by choice' | '/' || '/' - 'some xpath by parent' | '/some/xpath' || '/some/xpath' + scenario | inputXpath | contentType || xpathServiceParameter | requestBody | expectedData | expectedContentType + 'JSON content: root node by default' | '' | MediaType.APPLICATION_JSON || '/' | requestBodyJson | expectedJsonData | ContentType.JSON + 'JSON content: root node by choice' | '/' | MediaType.APPLICATION_JSON || '/' | requestBodyJson | expectedJsonData | ContentType.JSON + 'JSON content: some xpath by parent' | '/some/xpath' | MediaType.APPLICATION_JSON || '/some/xpath' | requestBodyJson | expectedJsonData | ContentType.JSON + 'XML content: root node by default' | '' | MediaType.APPLICATION_XML || '/' | requestBodyXml | expectedXmlData | ContentType.XML + 'XML content: root node by choice' | '/' | MediaType.APPLICATION_XML || '/' | requestBodyXml | expectedXmlData | ContentType.XML + 'XML content: some xpath by parent' | '/some/xpath' | MediaType.APPLICATION_XML || '/some/xpath' | requestBodyXml | expectedXmlData | ContentType.XML } def 'Update data node and descendants with observedTimestamp.'() { @@ -485,7 +488,7 @@ class DataRestControllerSpec extends Specification { .andReturn().response then: 'the service method is invoked with expected parameters' expectedApiCount * mockCpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, '/', expectedJsonData, - { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }) + { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, ContentType.JSON) and: 'response status indicates success' response.status == expectedHttpStatus.value() where: 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 f396b49e6b..cfa5f2de20 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 @@ -55,7 +55,7 @@ public interface CpsDataService { * @param anchorName anchor name * @param nodeData node data * @param observedTimestamp observedTimestamp - * @param contentType node data content type + * @param contentType JSON/XML content type */ void saveData(String dataspaceName, String anchorName, String nodeData, OffsetDateTime observedTimestamp, ContentType contentType); @@ -80,7 +80,7 @@ public interface CpsDataService { * @param parentNodeXpath parent node xpath * @param nodeData node data * @param observedTimestamp observedTimestamp - * @param contentType node data content type + * @param contentType JSON/XML content type * */ void saveData(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData, @@ -134,7 +134,7 @@ public interface CpsDataService { * @param parentNodeXpath xpath to parent node * @param nodeData node data * @param observedTimestamp observedTimestamp - * @param contentType node data content type + * @param contentType JSON/XML content type */ void updateNodeLeaves(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData, OffsetDateTime observedTimestamp, ContentType contentType); @@ -145,22 +145,24 @@ public interface CpsDataService { * @param dataspaceName dataspace name * @param anchorName anchor name * @param parentNodeXpath xpath to parent node - * @param jsonData json data + * @param nodeData node data * @param observedTimestamp observedTimestamp + * @param contentType JSON/XML content type */ - void updateDataNodeAndDescendants(String dataspaceName, String anchorName, String parentNodeXpath, String jsonData, - OffsetDateTime observedTimestamp); + void updateDataNodeAndDescendants(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData, + OffsetDateTime observedTimestamp, ContentType contentType); /** * Replaces multiple existing data nodes' content including descendants in a batch operation. * * @param dataspaceName dataspace name * @param anchorName anchor name - * @param nodesJsonData map of xpath and node JSON data + * @param nodeDataPerXPath map of xpath and node JSON/XML data * @param observedTimestamp observedTimestamp + * @param contentType JSON/XML content type */ - void updateDataNodesAndDescendants(String dataspaceName, String anchorName, Map<String, String> nodesJsonData, - OffsetDateTime observedTimestamp); + void updateDataNodesAndDescendants(String dataspaceName, String anchorName, Map<String, String> nodeDataPerXPath, + OffsetDateTime observedTimestamp, ContentType contentType); /** * Replaces list content by removing all existing elements and inserting the given new elements as json 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 5a48428772..c65bc5e0e9 100644 --- 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 @@ -251,12 +251,12 @@ public class CpsDataServiceImpl implements CpsDataService { @Timed(value = "cps.data.service.datanode.descendants.update", description = "Time taken to update a data node and descendants") public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName, - final String parentNodeXpath, final String jsonData, - final OffsetDateTime observedTimestamp) { + final String parentNodeXpath, final String nodeData, + final OffsetDateTime observedTimestamp, final ContentType contentType) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection<DataNode> dataNodes = - buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON); + buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType); cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes); sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp); } @@ -265,13 +265,13 @@ public class CpsDataServiceImpl implements CpsDataService { @Timed(value = "cps.data.service.datanode.descendants.batch.update", description = "Time taken to update a batch of data nodes and descendants") public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName, - final Map<String, String> nodesJsonData, - final OffsetDateTime observedTimestamp) { + final Map<String, String> nodeDataPerXPath, + final OffsetDateTime observedTimestamp, final ContentType contentType) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); - final Collection<DataNode> dataNodes = buildDataNodesWithParentNodeXpath(anchor, nodesJsonData); + final Collection<DataNode> dataNodes = buildDataNodesWithParentNodeXpath(anchor, nodeDataPerXPath, contentType); cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes); - nodesJsonData.keySet().forEach(nodeXpath -> + nodeDataPerXPath.keySet().forEach(nodeXpath -> sendDataUpdatedEvent(anchor, nodeXpath, Operation.UPDATE, observedTimestamp)); } @@ -408,11 +408,12 @@ public class CpsDataServiceImpl implements CpsDataService { } private Collection<DataNode> buildDataNodesWithParentNodeXpath(final Anchor anchor, - final Map<String, String> nodesJsonData) { + final Map<String, String> nodesJsonData, + final ContentType contentType) { final Collection<DataNode> dataNodes = new ArrayList<>(); for (final Map.Entry<String, String> nodeJsonData : nodesJsonData.entrySet()) { dataNodes.addAll(buildDataNodesWithParentNodeXpath(anchor, nodeJsonData.getKey(), - nodeJsonData.getValue(), ContentType.JSON)); + nodeJsonData.getValue(), contentType)); } return dataNodes; } 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 4e5807ec84..2e38d797d9 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 @@ -362,11 +362,11 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) } - def 'Replace data node using singular data node: #scenario.'() { + def 'Replace data node using singular JSON data node: #scenario.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') when: 'replace data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath' - objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp) + objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp, ContentType.JSON) then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, { dataNode -> dataNode.xpath == expectedNodeXpath}) @@ -379,30 +379,63 @@ class CpsDataServiceImplSpec extends Specification { 'json list' | '/test-tree' | '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] } - def 'Replace data node using multiple data nodes: #scenario.'() { + def 'Replace data node using singular XML data node: #scenario.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'replace data method is invoked with XML data #xmlData and parent node xpath #parentNodeXpath' + objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, xmlData, observedTimestamp, ContentType.XML) + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, + { dataNode -> dataNode.xpath == expectedNodeXpath }) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) + where: 'following parameters were used' + scenario | parentNodeXpath | xmlData || expectedNodeXpath + 'level 2 node' | '/test-tree' | '<branch><name>Name</name></branch>' || ['/test-tree/branch[@name=\'Name\']'] + 'xml list' | '/test-tree' | '<test-tree xmlns="org:onap:cps:test:test-tree"><branch><name>Name1</name></branch>' + '<branch><name>Name2</name></branch></test-tree>' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] + } + + def 'Replace data node using multiple JSON data nodes: #scenario.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') when: 'replace data method is invoked with a map of xpaths and json data' - objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodesJsonData, observedTimestamp) + objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodeDataPerXPath, observedTimestamp, ContentType.JSON) then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, { dataNode -> dataNode.xpath == expectedNodeXpath}) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'following parameters were used' - scenario | nodesJsonData || expectedNodeXpath + scenario | nodeDataPerXPath || expectedNodeXpath 'top level node' | ['/' : '{"test-tree": {"branch": []}}', '/test-tree' : '{"branch": [{"name":"Name"}]}'] || ["/test-tree", "/test-tree/branch[@name='Name']"] 'level 2 node' | ['/test-tree' : '{"branch": [{"name":"Name"}]}', '/test-tree/branch[@name=\'Name\']':'{"nest":{"name":"nestName"}}'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"] 'json list' | ['/test-tree' : '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}'] || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] } + def 'Replace data node using multiple XML data nodes: #scenario.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'replace data method is invoked with a map of xpaths and XML data' + objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodeXmlDataPerXPath, observedTimestamp, ContentType.XML) + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, + { dataNode -> dataNode.xpath == expectedNodeXpath }) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) + where: 'following parameters were used' + scenario | nodeXmlDataPerXPath || expectedNodeXpath + 'top level node' | ['/test-tree': '<branch><name>Name</name></branch>'] || ["/test-tree/branch[@name='Name']"] + 'level 2 node' | ['/test-tree': '<branch><name>Name</name></branch>', '/test-tree/branch[@name=\'Name\']': '<nest><name>nestName</name></nest>'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"] + 'xml list' | ['/test-tree': '<test-tree xmlns="org:onap:cps:test:test-tree"><branch><name>Name1</name></branch>' + '<branch><name>Name2</name></branch></test-tree>'] || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] + } + def 'Replace data node with concurrency exception in persistence layer.'() { given: 'the persistence layer throws an concurrency exception' def originalException = new ConcurrencyException('message', 'details') mockCpsDataPersistenceService.updateDataNodesAndDescendants(*_) >> { throw originalException } setupSchemaSetMocks('test-tree.yang') when: 'attempt to replace data node' - objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/' : '{"test-tree": {}}'] , observedTimestamp) + objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/' : '{"test-tree": {}}'] , observedTimestamp, ContentType.JSON) then: 'the same exception is thrown up' def thrownUp = thrown(ConcurrencyException) assert thrownUp == originalException diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy index a488b3b836..869b72d8ce 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy @@ -396,7 +396,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase { objectUnderTest.saveData(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now) when: 'the webinfo (container) is updated' json = '{"webinfo": {"domain-name":"newdomain.com" ,"contact-email":"info@newdomain.com" }}' - objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now) + objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now, ContentType.JSON) then: 'webinfo has been updated with teh new details' def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/webinfo', DIRECT_CHILDREN_ONLY) result.leaves.'domain-name'[0] == 'newdomain.com' @@ -408,7 +408,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase { 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) + objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/', json, now, ContentType.JSON) 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' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy index 7bcec968e5..03abdb4b3f 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy @@ -50,7 +50,7 @@ class UpdatePerfTest extends CpsPerfTestBase { given: 'replacement JSON for node containing list of device nodes' def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}' when: 'the container node is updated' - objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now) + objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now, ContentType.JSON) then: 'there are the expected number of total nodes' assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device') where: @@ -68,7 +68,7 @@ class UpdatePerfTest extends CpsPerfTestBase { def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}' when: 'the container node is updated' resourceMeter.start() - objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now) + objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now, ContentType.JSON) resourceMeter.stop() then: 'there are the expected number of total nodes' assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device') |