From 6ce84d98f68b45f02f16dc99423670f4a53fd946 Mon Sep 17 00:00:00 2001 From: Michal Jagiello Date: Tue, 13 Dec 2022 07:40:19 +0000 Subject: XML content on create anchors node support Add XML content type support on anchor node creation. Issue-ID: CPS-1257 Change-Id: I7e7a9a1961b6e81de93a4e32e842b47f8a163a09 Signed-off-by: Michal Jagiello Signed-off-by: Lee Anjella Macabuhay --- .../cps/api/impl/CpsDataServiceImplSpec.groovy | 38 +++++++++++++- .../org/onap/cps/utils/XmlFileUtilsSpec.groovy | 61 ++++++++++++++++++++++ .../groovy/org/onap/cps/utils/YangUtilsSpec.groovy | 54 ++++++++++++++----- cps-service/src/test/resources/bookstore.xml | 19 +++++++ cps-service/src/test/resources/bookstore_xpath.xml | 17 ++++++ cps-service/src/test/resources/test-tree.xml | 27 ++++++++++ 6 files changed, 200 insertions(+), 16 deletions(-) create mode 100644 cps-service/src/test/groovy/org/onap/cps/utils/XmlFileUtilsSpec.groovy create mode 100644 cps-service/src/test/resources/bookstore.xml create mode 100644 cps-service/src/test/resources/bookstore_xpath.xml create mode 100644 cps-service/src/test/resources/test-tree.xml (limited to 'cps-service/src/test') 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 b78ab8a45..c81a50ea7 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 @@ -4,7 +4,7 @@ * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada. * Modifications Copyright (C) 2022 TechMahindra Ltd. - * ================================================================================ + * Modifications Copyright (C) 2022 Deutsche Telekom AG * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -33,6 +33,7 @@ import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.Anchor import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder +import org.onap.cps.utils.ContentType import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder import spock.lang.Specification @@ -61,7 +62,7 @@ class CpsDataServiceImplSpec extends Specification { def anchor = Anchor.builder().name(anchorName).schemaSetName(schemaSetName).build() def observedTimestamp = OffsetDateTime.now() - def 'Saving json data.'() { + def 'Saving multicontainer json data.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('multipleDataTree.yang') when: 'save data method is invoked with test-tree json data' @@ -81,6 +82,39 @@ class CpsDataServiceImplSpec extends Specification { } + def 'Saving #scenario data.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'save data method is invoked with test-tree #scenario data' + def data = TestUtils.getResourceFileContent(dataFile) + objectUnderTest.saveData(dataspaceName, anchorName, data, observedTimestamp, contentType) + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, + { dataNode -> dataNode.xpath[0] == '/test-tree' }) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) + and: 'data updated event is sent to notification service' + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/', Operation.CREATE, observedTimestamp) + where: 'given parameters' + scenario | dataFile | contentType + 'json' | 'test-tree.json' | ContentType.JSON + 'xml' | 'test-tree.xml' | ContentType.XML + } + + def 'Saving #scenarioDesired data with invalid data.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'save data method is invoked with test-tree json data' + objectUnderTest.saveData(dataspaceName, anchorName, invalidData, observedTimestamp, contentType) + then: 'a data validation exception is thrown' + thrown(DataValidationException) + where: 'given parameters' + scenarioDesired | invalidData | contentType + 'json' | '{invalid json' | ContentType.XML + 'xml' | ' ' || ' ' + 'with root data node' | ' ' || ' ' + 'no xml header' | ' ' || ' ' + } + + def 'Parse a xml content with XPath container #scenario'() { + given: 'YANG model schema context' + def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('test-tree.yang') + def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() + and: 'Parent schema node by xPath' + def parentSchemaNode = YangUtils.getDataSchemaNodeAndIdentifiersByXpath(xPath, schemaContext) + .get("dataSchemaNode") + when: 'the XML data is parsed' + def parsedXmlContent = XmlFileUtils.prepareXmlContent(xmlData, parentSchemaNode, xPath) + then: 'the result XML is wrapped by xPath defined parent root node' + assert parsedXmlContent == expectedOutput + where: + scenario | xmlData | xPath || expectedOutput + 'XML element test tree' | 'LeftSmallSparrow' | '/test-tree' || 'LeftSmallSparrow' + 'without root data node' | 'SmallSparrow' | '/test-tree/branch[@name=\'Branch\']' || 'BranchSmallSparrow' + + + } + +} diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy index 990b7186f..bf6e134a6 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy @@ -3,6 +3,7 @@ * Copyright (C) 2020-2022 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2022 TechMahindra Ltd. + * Modifications Copyright (C) 2022 Deutsche Telekom AG * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +31,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode import spock.lang.Specification class YangUtilsSpec extends Specification { - def 'Parsing a valid Json String.'() { + def 'Parsing a valid multicontainer Json String.'() { given: 'a yang model (file)' def jsonData = org.onap.cps.TestUtils.getResourceFileContent('multiple-object-data.json') and: 'a model for that data' @@ -48,36 +49,62 @@ class YangUtilsSpec extends Specification { 1 | 'last-container' } + def 'Parsing a valid #scenario String.'() { + given: 'a yang model (file)' + def fileData = org.onap.cps.TestUtils.getResourceFileContent(contentFile) + and: 'a model for that data' + def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') + def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() + when: 'the data is parsed' + NormalizedNode result = YangUtils.parseData(contentType, fileData, schemaContext) + then: 'the result is a normalized node of the correct type' + if (revision) { + result.identifier.nodeType == QName.create(namespace, revision, localName) + } else { + result.identifier.nodeType == QName.create(namespace, localName) + } + where: + scenario | contentFile | contentType | namespace | revision | localName + 'JSON' | 'bookstore.json' | ContentType.JSON | 'org:onap:ccsdk:sample' | '2020-09-15' | 'bookstore' + 'XML' | 'bookstore.xml' | ContentType.XML | 'urn:ietf:params:xml:ns:netconf:base:1.0' | '' | 'bookstore' + } + def 'Parsing invalid data: #description.'() { given: 'a yang model (file)' def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() when: 'invalid data is parsed' - YangUtils.parseJsonData(invalidJson, schemaContext) + YangUtils.parseData(contentType, invalidData, schemaContext) then: 'an exception is thrown' thrown(DataValidationException) - where: 'the following invalid json is provided' - invalidJson | description - '{incomplete json' | 'incomplete json' - '{"test:bookstore": {"address": "Parnell st." }}' | 'json with un-modelled data' - '{" }' | 'json with syntax exception' + where: 'the following invalid data is provided' + invalidData | contentType | description + '{incomplete json' | ContentType.JSON | 'incomplete json' + '{"test:bookstore": {"address": "Parnell st." }}' | ContentType.JSON | 'json with un-modelled data' + '{" }' | ContentType.JSON | 'json with syntax exception' + '' | ContentType.XML | 'incomplete xml' + 'blabla' | ContentType.XML | 'xml with invalid model' + '' | ContentType.XML | 'empty xml' } - def 'Parsing json data fragment by xpath for #scenario.'() { + def 'Parsing data fragment by xpath for #scenario.'() { given: 'schema context' def yangResourcesMap = TestUtils.getYangResourcesAsMap('test-tree.yang') def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext() when: 'json string is parsed' - def result = YangUtils.parseJsonData(jsonData, schemaContext, parentNodeXpath) + def result = YangUtils.parseData(contentType, nodeData, schemaContext, parentNodeXpath) then: 'a ContainerNode holding collection of normalized nodes is returned' result.body().getAt(0) instanceof NormalizedNode == true then: 'result represents a node of expected type' result.body().getAt(0).getIdentifier().nodeType == QName.create('org:onap:cps:test:test-tree', '2020-02-02', nodeName) where: - scenario | jsonData | parentNodeXpath || nodeName - 'list element as container' | '{ "branch": { "name": "B", "nest": { "name": "N", "birds": ["bird"] } } }' | '/test-tree' || 'branch' - 'list element within list' | '{ "branch": [{ "name": "B", "nest": { "name": "N", "birds": ["bird"] } }] }' | '/test-tree' || 'branch' - 'container element' | '{ "nest": { "name": "N", "birds": ["bird"] } }' | '/test-tree/branch[@name=\'Branch\']' || 'nest' + scenario | contentType | nodeData | parentNodeXpath || nodeName + 'JSON list element as container' | ContentType.JSON | '{ "branch": { "name": "B", "nest": { "name": "N", "birds": ["bird"] } } }' | '/test-tree' || 'branch' + 'JSON list element within list' | ContentType.JSON | '{ "branch": [{ "name": "B", "nest": { "name": "N", "birds": ["bird"] } }] }' | '/test-tree' || 'branch' + 'JSON container element' | ContentType.JSON | '{ "nest": { "name": "N", "birds": ["bird"] } }' | '/test-tree/branch[@name=\'Branch\']' || 'nest' + 'XML element test tree' | ContentType.XML | 'LeftSmallSparrow' | '/test-tree' || 'branch' + 'XML element branch xpath' | ContentType.XML | 'LeftSmallSparrowRobin' | '/test-tree' || 'branch' + 'XML container element' | ContentType.XML | 'SmallSparrow' | '/test-tree/branch[@name=\'Branch\']' || 'nest' } def 'Parsing json data fragment by xpath error scenario: #scenario.'() { @@ -135,5 +162,4 @@ class YangUtilsSpec extends Specification { 'xpath contains list attribute' | '/test-tree/branch[@name=\'Branch\']' || ['test-tree','branch'] 'xpath contains list attributes with /' | '/test-tree/branch[@name=\'/Branch\']/categories[@id=\'/broken\']' || ['test-tree','branch','categories'] } - } diff --git a/cps-service/src/test/resources/bookstore.xml b/cps-service/src/test/resources/bookstore.xml new file mode 100644 index 000000000..dd45e1689 --- /dev/null +++ b/cps-service/src/test/resources/bookstore.xml @@ -0,0 +1,19 @@ + + + + Chapters + + 1 + SciFi + + 2001: A Space Odyssey + en + + Iain M. Banks + + 1994 + 895 + + + + \ No newline at end of file diff --git a/cps-service/src/test/resources/bookstore_xpath.xml b/cps-service/src/test/resources/bookstore_xpath.xml new file mode 100644 index 000000000..e206901d6 --- /dev/null +++ b/cps-service/src/test/resources/bookstore_xpath.xml @@ -0,0 +1,17 @@ + + + Chapters + + 1 + SciFi + + 2001: A Space Odyssey + en + + Iain M. Banks + + 1994 + 895 + + + \ No newline at end of file diff --git a/cps-service/src/test/resources/test-tree.xml b/cps-service/src/test/resources/test-tree.xml new file mode 100644 index 000000000..3daa814cf --- /dev/null +++ b/cps-service/src/test/resources/test-tree.xml @@ -0,0 +1,27 @@ + + + + + Left + + Small + Sparrow + Robin + Finch + + + + Right + + Big + Owl + Raven + Crow + + + + Apple + Green + + + -- cgit 1.2.3-korg