From 099f89eda3b0c94d20612ebf1d62795ab7dc833a Mon Sep 17 00:00:00 2001 From: ToineSiebelink Date: Mon, 25 Jan 2021 18:46:20 +0000 Subject: Draft at proposal where the DataNodeBuilder 'replaces' yang Utils to buidl a DataNode Most complexity is related to immutable collections and the fact taht we are adding data while recursing over the orignal data in an uncontrolled order. I cleaned it up as best I could with no logic in DataNode. Espcially the handling of LitLeaves requires some specialed handling. Thsi is just a draft solution for that I still propose we get back to that in dedicated user stories for handling the various types of Yang elements Hope this helps... Issue-ID: CPS-137 Signed-off-by: ToineSiebelink Change-Id: Iab7cfcff67412c01bcdab95e707e1350bf60fab1 --- .../spi/impl/CpsDataPersistenceServiceTest.java | 17 +- .../main/java/org/onap/cps/api/impl/Fragment.java | 162 ------------------- .../main/java/org/onap/cps/spi/model/DataNode.java | 24 +-- .../org/onap/cps/spi/model/DataNodeBuilder.java | 176 +++++++++++++++++++++ .../main/java/org/onap/cps/utils/YangUtils.java | 102 ++---------- .../org/onap/cps/model/DataNodeBuilderSpec.groovy | 29 ++++ .../groovy/org/onap/cps/utils/YangUtilsSpec.groovy | 21 --- 7 files changed, 244 insertions(+), 287 deletions(-) delete mode 100644 cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java create mode 100644 cps-service/src/main/java/org/onap/cps/spi/model/DataNodeBuilder.java create mode 100644 cps-service/src/test/groovy/org/onap/cps/model/DataNodeBuilderSpec.groovy diff --git a/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceTest.java b/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceTest.java index 4501e5f0ce..5c402917d4 100644 --- a/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceTest.java +++ b/cps-ri/src/test/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceTest.java @@ -23,7 +23,6 @@ import static junit.framework.TestCase.assertEquals; import com.google.common.collect.ImmutableSet; import java.util.Arrays; -import java.util.Collections; import org.assertj.core.api.Assertions; import org.junit.ClassRule; import org.junit.Test; @@ -35,6 +34,7 @@ import org.onap.cps.spi.exceptions.AnchorNotFoundException; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataspaceNotFoundException; import org.onap.cps.spi.model.DataNode; +import org.onap.cps.spi.model.DataNodeBuilder; import org.onap.cps.spi.repository.FragmentRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -42,7 +42,6 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.junit4.SpringRunner; - @RunWith(SpringRunner.class) @SpringBootTest public class CpsDataPersistenceServiceTest { @@ -67,7 +66,6 @@ public class CpsDataPersistenceServiceTest { private static final long GRAND_CHILD_ID_4006 = 4006; private static final String GRAND_CHILD_XPATH1 = "/parent-1/child-1/grandchild-1"; - @ClassRule public static DatabaseTestContainer databaseTestContainer = DatabaseTestContainer.getInstance(); @@ -91,14 +89,16 @@ public class CpsDataPersistenceServiceTest { @Sql({CLEAR_DATA, SET_DATA}) public void testStoreDataNodeAtNonExistingDataspace() { cpsDataPersistenceService - .storeDataNode("Non Existing Dataspace Name", ANCHOR_NAME1, new DataNode()); + .storeDataNode("Non Existing Dataspace Name", ANCHOR_NAME1, + new DataNodeBuilder().build()); } @Test(expected = AnchorNotFoundException.class) @Sql({CLEAR_DATA, SET_DATA}) public void testStoreDataNodeAtNonExistingAnchor() { cpsDataPersistenceService - .storeDataNode(DATASPACE_NAME, "Non Existing Anchor Name", new DataNode()); + .storeDataNode(DATASPACE_NAME, "Non Existing Anchor Name", + new DataNodeBuilder().build()); } @Test(expected = DataIntegrityViolationException.class) @@ -188,12 +188,13 @@ public class CpsDataPersistenceServiceTest { } private static DataNode createDataNodeTree(final String... xpaths) { - final DataNode dataNode = DataNode.builder().xpath(xpaths[0]).childDataNodes(Collections.emptySet()).build(); + final DataNodeBuilder dataNodeBuilder = new DataNodeBuilder().withXpath(xpaths[0]); if (xpaths.length > 1) { final String[] xPathsDescendant = Arrays.copyOfRange(xpaths, 1, xpaths.length); final DataNode childDataNode = createDataNodeTree(xPathsDescendant); - dataNode.setChildDataNodes(ImmutableSet.of(childDataNode)); + dataNodeBuilder.withChildDataNodes(ImmutableSet.of(childDataNode)); + } - return dataNode; + return dataNodeBuilder.build(); } } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java b/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java deleted file mode 100644 index 0f785b718f..0000000000 --- a/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2020 Nordix Foundation - * ================================================================================ - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.api.impl; - -import com.google.common.collect.ImmutableList; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import lombok.Getter; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.Module; - -/** - * Class to store a Yang Fragment (container or list element). - */ -public class Fragment { - - @Getter - private final String xpath; - - @Getter - private final Map attributes = new HashMap<>(); - - @Getter - private final Module module; - - @Getter - private final Fragment parentFragment; - - @Getter - private final Set childFragments = new HashSet<>(0); - - private final QName[] qnames; - - private Optional> optionalLeafListNames = Optional.empty(); - - /** - * Create a root Fragment. - * - * @param module the Yang module that encompasses this fragment - * @param qnames the list of qualified names that points the schema node for this fragment - * @param xpath the xpath of root fragment - */ - public static Fragment createRootFragment(final Module module, final QName[] qnames, final String xpath) { - return new Fragment(null, module, qnames, xpath); - } - - /** - * Create a Child Fragment under a given Parent Fragment. - * - * @param parentFragment the parent (can be null for 'root' objects) - * @param module the Yang module that encompasses this fragment - * @param qnames array of qualified names that points the schema node for this fragment - * @param xpath the xpath for this fragment - */ - private Fragment(final Fragment parentFragment, final Module module, final QName[] qnames, final String xpath) { - this.parentFragment = parentFragment; - this.module = module; - this.qnames = qnames; - this.xpath = xpath; - } - - /** - * Create a Child Fragment where the current Fragment is the parent. - * - * @param childQname The Qualified name for the child (relative to the parent) - * @param childXPath The child xpath (relative to the parrent) - * @return the child fragment - */ - public Fragment createChildFragment(final QName childQname, final String childXPath) { - final QName[] qnamesForChild = Arrays.copyOf(qnames, qnames.length + 1); - qnamesForChild[qnamesForChild.length - 1] = childQname; - final Fragment childFragment = new Fragment(this, module, qnamesForChild, getXpath() + childXPath); - childFragments.add(childFragment); - return childFragment; - } - - /** - * Define a leaf list by providing its name. - * The list is not instantiated until the first value is provided - * - * @param name the name of the leaf list - */ - public void addLeafListName(final String name) { - if (optionalLeafListNames.isEmpty()) { - optionalLeafListNames = Optional.of(new HashSet<>()); - } - optionalLeafListNames.get().add(name); - } - - /** - * Add a leaf or leaf list value. - * For Leaf lists it is essential to first define the attribute is a leaf list by using addLeafListName method - * - * @param name the name of the leaf (or leaf list) - * @param value the value of the leaf (or element of leaf list) - */ - public void addLeafValue(final String name, final Object value) { - if (optionalLeafListNames.isPresent() && optionalLeafListNames.get().contains(name)) { - addLeafListValue(name, value); - } else { - attributes.put(name, value); - } - } - - private void addLeafListValue(final String name, final Object value) { - if (attributes.containsKey(name)) { - final ImmutableList oldList = (ImmutableList) attributes.get(name); - final List newList = new ArrayList<>(oldList); - newList.add(value); - attributes.put(name, ImmutableList.copyOf(newList)); - } else { - attributes.put(name, ImmutableList.of(value)); - } - } - - /** - * Get the SchemaNodeIdentifier for this fragment. - * - * @return the SchemaNodeIdentifier - */ - public String getSchemaNodeIdentifier() { - final StringBuilder stringBuilder = new StringBuilder(); - for (final QName qname : qnames) { - stringBuilder.append(qname.getLocalName()); - stringBuilder.append('/'); - } - return stringBuilder.toString(); - } - - /** - * Get the Optional SchemaNode (model) for this data fragment. - * - * @return the Optional SchemaNode - */ - public Optional getSchemaNode() { - return module.findDataTreeChild(qnames); - } -} diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java b/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java index e9c6b56ea5..721a7c0426 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java +++ b/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java @@ -1,6 +1,7 @@ /*- * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation. All rights reserved. + * Modifications Copyright (C) 2021 Bell Canada. 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. @@ -21,24 +22,27 @@ package org.onap.cps.spi.model; import java.util.Collection; +import java.util.Collections; import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import java.util.Optional; +import java.util.Set; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor +@Setter(AccessLevel.PROTECTED) +@Getter public class DataNode { + DataNode() { } + private String dataspace; private String schemaSetName; private String anchorName; private ModuleReference moduleReference; private String xpath; - private Map leaves; + private Map leaves = Collections.emptyMap(); private Collection xpathsChildren; - private Collection childDataNodes; + private Collection childDataNodes = Collections.emptySet(); + private Optional> optionalLeafListNames = Optional.empty(); } diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DataNodeBuilder.java b/cps-service/src/main/java/org/onap/cps/spi/model/DataNodeBuilder.java new file mode 100644 index 0000000000..cd6a3a2201 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/spi/model/DataNodeBuilder.java @@ -0,0 +1,176 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Bell Canada. 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.spi.model; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.utils.YangUtils; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.ValueNode; + +@Slf4j +public class DataNodeBuilder { + + private NormalizedNode normalizedNodeTree; + private String xpath; + private Collection childDataNodes = Collections.emptySet(); + + + /** To use {@link NormalizedNode} for creating {@link DataNode}. + * + * @param normalizedNodeTree used for creating the Data Node + * + * @return this {@link DataNodeBuilder} object + */ + public DataNodeBuilder withNormalizedNodeTree(final NormalizedNode normalizedNodeTree) { + this.normalizedNodeTree = normalizedNodeTree; + return this; + } + + /** + * To use xpath for creating {@link DataNode}. + * @param xpath for the data node + * @return DataNodeBuilder + */ + public DataNodeBuilder withXpath(final String xpath) { + this.xpath = xpath; + return this; + } + + /** + * To specify child nodes needs to be used while creating {@link DataNode}. + * @param childDataNodes to be added to the dataNode + * @return DataNodeBuilder + */ + public DataNodeBuilder withChildDataNodes(final Collection childDataNodes) { + // Added as this is being set from test cases . + // Open for suggestions + this.childDataNodes = childDataNodes; + return this; + } + + /** + * To create the {@link DataNode}. + * + * @return {@link DataNode} + */ + public DataNode build() { + if (normalizedNodeTree != null) { + return buildFromNormalizedNodeTree(); + } else { + return buildFromAttributes(); + } + } + + private DataNode buildFromAttributes() { + final DataNode dataNode = new DataNode(); + dataNode.setXpath(xpath); + dataNode.setChildDataNodes(childDataNodes); + return dataNode; + } + + private DataNode buildFromNormalizedNodeTree() { + xpath = YangUtils.buildXpath(normalizedNodeTree.getIdentifier()); + final DataNode dataNode = new DataNodeBuilder().withXpath(xpath).build(); + addDataNodeFromNormalizedNode(dataNode, normalizedNodeTree); + return dataNode; + } + + private void addDataNodeFromNormalizedNode(final DataNode currentDataNode, + final NormalizedNode normalizedNode) { + if (normalizedNode instanceof DataContainerNode) { + addYangContainer(currentDataNode, (DataContainerNode) normalizedNode); + } else if (normalizedNode instanceof MapNode) { + addDataNodeForEachListElement(currentDataNode, (MapNode) normalizedNode); + } else if (normalizedNode instanceof ValueNode) { + final ValueNode valuesNode = (ValueNode) normalizedNode; + addYangLeaf(currentDataNode, valuesNode.getNodeType().getLocalName(), valuesNode.getValue()); + } else if (normalizedNode instanceof LeafSetNode) { + addYangLeafList(currentDataNode, (LeafSetNode) normalizedNode); + } else { + log.warn("Cannot normalize {}", normalizedNode.getClass()); + } + } + + private void addYangContainer(final DataNode currentDataNode, final DataContainerNode dataContainerNode) { + final Collection normalizedChildNodes = dataContainerNode.getValue(); + for (final NormalizedNode normalizedNode : normalizedChildNodes) { + addDataNodeFromNormalizedNode(currentDataNode, normalizedNode); + } + } + + private void addYangLeaf(final DataNode currentDataNode, final String leafName, final Object leafValue) { + final Map leaves = new ImmutableMap.Builder() + .putAll(currentDataNode.getLeaves()) + .put(leafName, leafValue) + .build(); + currentDataNode.setLeaves(leaves); + } + + private void addYangLeafList(final DataNode currentDataNode, final LeafSetNode leafSetNode) { + final ImmutableSet.Builder builder = new ImmutableSet.Builder(); + final String leafListName = leafSetNode.getNodeType().getLocalName(); + final Optional> optionalLeafListNames = currentDataNode.getOptionalLeafListNames(); + if (optionalLeafListNames.isPresent()) { + builder.addAll(optionalLeafListNames.get()); + } + builder.add(leafListName); + final ImmutableSet leafListNames = builder.build(); + currentDataNode.setOptionalLeafListNames(Optional.of(leafListNames)); + final List leafListValues = new LinkedList(); + for (final NormalizedNode normalizedNode : (Collection) leafSetNode.getValue()) { + leafListValues.add(((ValueNode) normalizedNode).getValue()); + } + addYangLeaf(currentDataNode, leafListName, leafListValues); + } + + private void addDataNodeForEachListElement(final DataNode currentDataNode, final MapNode mapNode) { + final Collection mapEntryNodes = mapNode.getValue(); + for (final MapEntryNode mapEntryNode : mapEntryNodes) { + final String xpathChild = YangUtils.buildXpath(mapEntryNode.getIdentifier()); + final DataNode childDataNode = createAndAddChildDataNode(currentDataNode, xpathChild); + addDataNodeFromNormalizedNode(childDataNode, mapEntryNode); + } + } + + private DataNode createAndAddChildDataNode(final DataNode parentDataNode, final String childXpath) { + final DataNode newChildDataNode = new DataNodeBuilder().withXpath(xpath + childXpath) + .build(); + final Set allChildDataNodes = new ImmutableSet.Builder() + .addAll(parentDataNode.getChildDataNodes()) + .add(newChildDataNode) + .build(); + parentDataNode.setChildDataNodes(allChildDataNodes); + return newChildDataNode; + } + +} diff --git a/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java b/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java index 8077ed7d88..1244d54afb 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java +++ b/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada. 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. @@ -22,35 +23,26 @@ package org.onap.cps.utils; import com.google.gson.stream.JsonReader; import java.io.IOException; import java.io.StringReader; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.api.impl.Fragment; -import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.ValueNode; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory; import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier; import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; -import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @Slf4j public class YangUtils { + private YangUtils() { - throw new IllegalStateException("Utility class"); + // Private constructor fo security reasons } /** @@ -60,7 +52,7 @@ public class YangUtils { * @param schemaContext the SchemaContext for the given data * @return the NormalizedNode representing the json data */ - public static NormalizedNode parseJsonData(final String jsonData, final SchemaContext schemaContext) + public static NormalizedNode parseJsonData(final String jsonData, final SchemaContext schemaContext) throws IOException { final JSONCodecFactory jsonCodecFactory = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02 .getShared(schemaContext); @@ -76,85 +68,23 @@ public class YangUtils { } /** - * Break a Normalized Node tree into fragments that can be stored by the persistence service. - * - * @param tree the normalized node tree - * @param module the module applicable for the data in the normalized node - * @return the 'root' Fragment for the tree contain all relevant children etc. + * Create an xpath form a Yang Tools NodeIdentifier (i.e. PathArgument). + * @param nodeIdentifier the NodeIdentifier + * @return an xpath */ - public static Fragment fragmentNormalizedNode( - final NormalizedNode tree, - final Module module) { - final QName[] nodeTypes = {tree.getNodeType()}; - final String xpath = buildXpathId(tree.getIdentifier()); - final Fragment rootFragment = Fragment.createRootFragment(module, nodeTypes, xpath); - fragmentNormalizedNode(rootFragment, tree); - return rootFragment; - } - - private static void fragmentNormalizedNode(final Fragment currentFragment, - final NormalizedNode normalizedNode) { - if (normalizedNode instanceof DataContainerNode) { - inspectContainer(currentFragment, (DataContainerNode) normalizedNode); - } else if (normalizedNode instanceof MapNode) { - inspectKeyedList(currentFragment, (MapNode) normalizedNode); - } else if (normalizedNode instanceof ValueNode) { - inspectLeaf(currentFragment, (ValueNode) normalizedNode); - } else if (normalizedNode instanceof LeafSetNode) { - inspectLeafList(currentFragment, (LeafSetNode) normalizedNode); - } else { - log.warn("Cannot normalize {}", normalizedNode.getClass()); - } - } - - private static void inspectLeaf(final Fragment currentFragment, - final ValueNode valueNode) { - final Object value = valueNode.getValue(); - currentFragment.addLeafValue(valueNode.getNodeType().getLocalName(), value); - } - - private static void inspectLeafList(final Fragment currentFragment, - final LeafSetNode leafSetNode) { - currentFragment.addLeafListName(leafSetNode.getNodeType().getLocalName()); - for (final NormalizedNode value : (Collection) leafSetNode.getValue()) { - fragmentNormalizedNode(currentFragment, value); - } - } - - private static void inspectContainer(final Fragment currentFragment, - final DataContainerNode dataContainerNode) { - final Collection leaves = (Collection) dataContainerNode.getValue(); - for (final NormalizedNode leaf : leaves) { - fragmentNormalizedNode(currentFragment, leaf); - } - } - - private static void inspectKeyedList(final Fragment currentFragment, - final MapNode mapNode) { - createNodeForEachListElement(currentFragment, mapNode); - } - - private static void createNodeForEachListElement(final Fragment currentFragment, final MapNode mapNode) { - final Collection mapEntryNodes = mapNode.getValue(); - for (final MapEntryNode mapEntryNode : mapEntryNodes) { - final String xpathId = buildXpathId(mapEntryNode.getIdentifier()); - final Fragment listElementFragment = - currentFragment.createChildFragment(mapNode.getNodeType(), xpathId); - fragmentNormalizedNode(listElementFragment, mapEntryNode); - } - } - - private static String buildXpathId(final YangInstanceIdentifier.PathArgument nodeIdentifier) { - final StringBuilder xpathIdBuilder = new StringBuilder(); - xpathIdBuilder.append("/").append(nodeIdentifier.getNodeType().getLocalName()); + public static String buildXpath(final YangInstanceIdentifier.PathArgument nodeIdentifier) { + final StringBuilder xpathBuilder = new StringBuilder(); + xpathBuilder.append("/").append(nodeIdentifier.getNodeType().getLocalName()); - if (nodeIdentifier instanceof NodeIdentifierWithPredicates) { - xpathIdBuilder.append(getKeyAttributesStatement((NodeIdentifierWithPredicates) nodeIdentifier)); + if (nodeIdentifier instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) { + xpathBuilder.append(getKeyAttributesStatement( + (YangInstanceIdentifier.NodeIdentifierWithPredicates) nodeIdentifier)); } - return xpathIdBuilder.toString(); + return xpathBuilder.toString(); } - private static String getKeyAttributesStatement(final NodeIdentifierWithPredicates nodeIdentifier) { + private static String getKeyAttributesStatement( + final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifier) { final List keyAttributes = nodeIdentifier.entrySet().stream().map( entry -> { final String name = entry.getKey().getLocalName(); diff --git a/cps-service/src/test/groovy/org/onap/cps/model/DataNodeBuilderSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/model/DataNodeBuilderSpec.groovy new file mode 100644 index 0000000000..0dbde889a4 --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/model/DataNodeBuilderSpec.groovy @@ -0,0 +1,29 @@ +package org.onap.cps.model + +import org.onap.cps.TestUtils +import org.onap.cps.spi.model.DataNodeBuilder +import org.onap.cps.utils.YangUtils +import org.onap.cps.yang.YangTextSchemaSourceSetBuilder +import spock.lang.Specification + +class DataNodeBuilderSpec extends Specification { + + def 'Converting Normalized Node (tree) to a DataNode (tree).'() { + given: 'a Yang module' + def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') + def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent)getSchemaContext() + and: 'a normalized node for that model' + def jsonData = TestUtils.getResourceFileContent('bookstore.json') + def normalizedNode = YangUtils.parseJsonData(jsonData, schemaContext) + when: 'the normalized node is converted to a DataNode (tree)' + def result = new DataNodeBuilder().withNormalizedNodeTree(normalizedNode).build() + then: 'the system creates a (root) fragment without a parent and 2 children (categories)' + result.childDataNodes.size() == 2 + and: 'each child (category) has the root fragment (result) as parent and in turn as 1 child (a list of books)' + result.childDataNodes.each { it.childDataNodes.size() == 1 } + and: 'the fragments have the correct xpaths' + assert result.xpath == '/bookstore' + assert result.childDataNodes.collect { it.xpath } + .containsAll(["/bookstore/categories[@code='01']", "/bookstore/categories[@code='02']"]) + } +} 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 9237fa29af..e6e5d6a366 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 @@ -54,25 +54,4 @@ class YangUtilsSpec extends Specification{ '{incomplete json' | 'incomplete json' '{"test:bookstore": {"address": "Parnell st." }}' | 'json with un-modelled data' } - - def 'Breaking a Json Data Object into fragments.'() { - given: 'a Yang module' - def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') - def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent)getSchemaContext() - def module = schemaContext.findModule('stores', Revision.of('2020-09-15')).get() - and: 'a normalized node for that model' - def jsonData = TestUtils.getResourceFileContent('bookstore.json') - def normalizedNode = YangUtils.parseJsonData(jsonData, schemaContext) - when: 'the json data is fragmented' - def result = YangUtils.fragmentNormalizedNode(normalizedNode, module) - then: 'the system creates a (root) fragment without a parent and 2 children (categories)' - result.parentFragment == null - result.childFragments.size() == 2 - and: 'each child (category) has the root fragment (result) as parent and in turn as 1 child (a list of books)' - result.childFragments.each { it.parentFragment == result && it.childFragments.size() == 1 } - and: 'the fragments have the correct xpaths' - assert result.xpath == '/bookstore' - assert result.childFragments.collect { it.xpath } - .containsAll(["/bookstore/categories[@code='01']", "/bookstore/categories[@code='02']"]) - } } -- cgit 1.2.3-korg