From: Jozef Gloncak Date: Tue, 17 Jun 2014 11:22:24 +0000 (+0200) Subject: Bug 484 - Anyxml in sal-rest-connector X-Git-Tag: release/helium~595 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=f5639ec4d80dc50b71f306137dd7aa9f077973b2 Bug 484 - Anyxml in sal-rest-connector - RestconfImpl: handled AnyXmlSchemaNode - JsonMapper: handled AnyXmlSchemaNode - CnSnToJsonBasicDataTypesTest: added test cases for anyxml. This commit depends (therefore is commited as draft) on commits: - Add Anyxml normalized node to yang-data-api - https://git.opendaylight.org/gerrit/#/c/7771/1 - Implementation of Anyxml builder - https://git.opendaylight.org/gerrit/#/c/8061/ Change-Id: Ib3da88cc6af96d98d139f7c1bbc94408ef9e814d Signed-off-by: tpantelis Signed-off-by: Jozef Gloncak --- diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java index 1e5bfbd6b9..696bf71535 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java @@ -9,15 +9,15 @@ package org.opendaylight.controller.sal.rest.impl; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Preconditions; +import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.net.URI; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; - import javax.activation.UnsupportedDataTypeException; - import org.opendaylight.controller.sal.core.api.mount.MountInstance; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO; @@ -29,6 +29,7 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; @@ -48,13 +49,8 @@ import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinit import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; -import com.google.gson.stream.JsonWriter; - class JsonMapper { - private final Set foundLeafLists = new HashSet<>(); - private final Set foundLists = new HashSet<>(); private MountInstance mountPoint; private final Logger logger = LoggerFactory.getLogger(JsonMapper.class); @@ -77,15 +73,14 @@ class JsonMapper { } writer.endObject(); - - foundLeafLists.clear(); - foundLists.clear(); } private void writeChildrenOfParent(final JsonWriter writer, final CompositeNode parent, final DataNodeContainer parentSchema) throws IOException { checkNotNull(parent); + final Set foundLists = new HashSet<>(); + Set parentSchemaChildNodes = parentSchema == null ? Collections.emptySet() : parentSchema.getChildNodes(); @@ -98,59 +93,88 @@ class JsonMapper { logger.debug( "No schema found for data node \"" + child.getNodeType() ); - handleNoSchemaFound( writer, child, parent ); + if( !foundLists.contains( child.getNodeType() ) ) { + handleNoSchemaFound( writer, child, parent ); + + // Since we don't have a schema, we don't know which nodes are supposed to be + // lists so treat every one as a potential list to avoid outputting duplicates. + + foundLists.add( child.getNodeType() ); + } } else if (childSchema instanceof ContainerSchemaNode) { Preconditions.checkState(child instanceof CompositeNode, "Data representation of Container should be CompositeNode - " + child.getNodeType()); writeContainer(writer, (CompositeNode) child, (ContainerSchemaNode) childSchema); } else if (childSchema instanceof ListSchemaNode) { - if (!foundLists.contains(childSchema)) { + if (!foundLists.contains( child.getNodeType() ) ) { Preconditions.checkState(child instanceof CompositeNode, "Data representation of List should be CompositeNode - " + child.getNodeType()); - foundLists.add((ListSchemaNode) childSchema); + foundLists.add( child.getNodeType() ); writeList(writer, parent, (CompositeNode) child, (ListSchemaNode) childSchema); } } else if (childSchema instanceof LeafListSchemaNode) { - if (!foundLeafLists.contains(childSchema)) { + if (!foundLists.contains( child.getNodeType() ) ) { Preconditions.checkState(child instanceof SimpleNode, "Data representation of LeafList should be SimpleNode - " + child.getNodeType()); - foundLeafLists.add((LeafListSchemaNode) childSchema); + foundLists.add( child.getNodeType() ); writeLeafList(writer, parent, (SimpleNode) child, (LeafListSchemaNode) childSchema); } } else if (childSchema instanceof LeafSchemaNode) { Preconditions.checkState(child instanceof SimpleNode, "Data representation of LeafList should be SimpleNode - " + child.getNodeType()); writeLeaf(writer, (SimpleNode) child, (LeafSchemaNode) childSchema); + } else if (childSchema instanceof AnyXmlSchemaNode) { + if( child instanceof CompositeNode ) { + writeContainer(writer, (CompositeNode) child, null); + } + else { + handleNoSchemaFound( writer, child, parent ); + } } else { throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode, ListSchemaNode, " + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet."); } } + } - for (Node child : parent.getValue()) { - DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchemaChildNodes); - if (childSchema instanceof LeafListSchemaNode) { - foundLeafLists.remove(childSchema); - } else if (childSchema instanceof ListSchemaNode) { - foundLists.remove(childSchema); - } + private void writeValue( final JsonWriter writer, Object value ) throws IOException { + if( value != null ) { + writer.value( String.valueOf( value ) ); + } + else { + writer.value( "" ); } } private void handleNoSchemaFound( final JsonWriter writer, final Node node, final CompositeNode parent ) throws IOException { if( node instanceof SimpleNode ) { - writeName( node, null, writer ); - Object value = node.getValue(); - if( value != null ) { - writer.value( String.valueOf( value ) ); + List> nodeLeafList = parent.getSimpleNodesByName( node.getNodeType() ); + if( nodeLeafList.size() == 1 ) { + writeName( node, null, writer ); + writeValue( writer, node.getValue() ); + } + else { // more than 1, write as a json array + writeName( node, null, writer ); + writer.beginArray(); + for( SimpleNode leafNode: nodeLeafList ) { + writeValue( writer, leafNode.getValue() ); + } + + writer.endArray(); } } else { // CompositeNode Preconditions.checkState( node instanceof CompositeNode, "Data representation of Container should be CompositeNode - " + node.getNodeType() ); - writeContainer( writer, (CompositeNode) node, null ); + List nodeList = parent.getCompositesByName( node.getNodeType() ); + if( nodeList.size() == 1 ) { + writeContainer( writer, (CompositeNode) node, null ); + } + else { // more than 1, write as a json array + writeList( writer, parent, (CompositeNode) node, null ); + } } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java index b0916f4500..6330c0a479 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java @@ -36,6 +36,7 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdent import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; @@ -804,7 +805,8 @@ public class ControllerContext implements SchemaContextListener { public boolean isInstantiatedDataSchema( final DataSchemaNode node ) { return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode || - node instanceof ContainerSchemaNode || node instanceof ListSchemaNode; + node instanceof ContainerSchemaNode || node instanceof ListSchemaNode || + node instanceof AnyXmlSchemaNode; } private void addKeyValue( final HashMap map, final DataSchemaNode node, diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index 9700d48bc2..4716a02be2 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -56,6 +56,7 @@ import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; @@ -1044,97 +1045,127 @@ public class RestconfImpl implements RestconfService { } } - if ((nodeBuilder instanceof CompositeNodeWrapper)) { - final List> children = ((CompositeNodeWrapper) nodeBuilder).getValues(); - for (final NodeWrapper child : children) { - final List potentialSchemaNodes = - this.controllerContext.findInstanceDataChildrenByName( - ((DataNodeContainer) schema), child.getLocalName()); + if ( nodeBuilder instanceof CompositeNodeWrapper ) { + if( schema instanceof DataNodeContainer ) { + normalizeCompositeNode( (CompositeNodeWrapper)nodeBuilder, (DataNodeContainer)schema, + mountPoint, currentAugment ); + } + else if( schema instanceof AnyXmlSchemaNode ) { + normalizeAnyXmlNode( (CompositeNodeWrapper)nodeBuilder, (AnyXmlSchemaNode)schema ); + } + } + else if ( nodeBuilder instanceof SimpleNodeWrapper ) { + normalizeSimpleNode( (SimpleNodeWrapper) nodeBuilder, schema, mountPoint ); + } + else if ((nodeBuilder instanceof EmptyNodeWrapper)) { + normalizeEmptyNode( (EmptyNodeWrapper) nodeBuilder, schema ); + } + } - if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) { - StringBuilder builder = new StringBuilder(); - for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) { - builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()) - .append("\n"); - } + private void normalizeAnyXmlNode( CompositeNodeWrapper compositeNode, AnyXmlSchemaNode schema ) { + List> children = compositeNode.getValues(); + for( NodeWrapper child : children ) { + child.setNamespace( schema.getQName().getNamespace() ); + if( child instanceof CompositeNodeWrapper ) { + normalizeAnyXmlNode( (CompositeNodeWrapper)child, schema ); + } + } + } - throw new RestconfDocumentedException( - "Node \"" + child.getLocalName() + - "\" is added as augment from more than one module. " + - "Therefore node must have namespace (XML format) or module name (JSON format)." + - "\nThe node is added as augment from modules with namespaces:\n" + builder, - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); - } + private void normalizeEmptyNode( EmptyNodeWrapper emptyNodeBuilder, DataSchemaNode schema ) { + if ((schema instanceof LeafSchemaNode)) { + emptyNodeBuilder.setComposite(false); + } + else { + if ((schema instanceof ContainerSchemaNode)) { + // FIXME: Add presence check + emptyNodeBuilder.setComposite(true); + } + } + } + + private void normalizeSimpleNode( SimpleNodeWrapper simpleNode, DataSchemaNode schema, + MountInstance mountPoint ) { + final Object value = simpleNode.getValue(); + Object inputValue = value; + TypeDefinition typeDefinition = this.typeDefinition(schema); + if ((typeDefinition instanceof IdentityrefTypeDefinition)) { + if ((value instanceof String)) { + inputValue = new IdentityValuesDTO( simpleNode.getNamespace().toString(), + (String) value, null, (String) value ); + } // else value is already instance of IdentityValuesDTO + } + + Object outputValue = inputValue; + + if( typeDefinition != null ) { + Codec codec = RestCodec.from(typeDefinition, mountPoint); + outputValue = codec == null ? null : codec.deserialize(inputValue); + } + + simpleNode.setValue(outputValue); + } - boolean rightNodeSchemaFound = false; + private void normalizeCompositeNode( CompositeNodeWrapper compositeNodeBuilder, + DataNodeContainer schema, MountInstance mountPoint, + QName currentAugment ) { + final List> children = compositeNodeBuilder.getValues(); + for (final NodeWrapper child : children) { + final List potentialSchemaNodes = + this.controllerContext.findInstanceDataChildrenByName( + schema, child.getLocalName()); + + if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) { + StringBuilder builder = new StringBuilder(); for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) { - if (!rightNodeSchemaFound) { - final QName potentialCurrentAugment = - this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint); - if (child.getQname() != null ) { - this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint); - rightNodeSchemaFound = true; - } - } + builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()) + .append("\n"); } - if (!rightNodeSchemaFound) { - throw new RestconfDocumentedException( - "Schema node \"" + child.getLocalName() + "\" was not found in module.", - ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT ); - } + throw new RestconfDocumentedException( + "Node \"" + child.getLocalName() + + "\" is added as augment from more than one module. " + + "Therefore node must have namespace (XML format) or module name (JSON format)." + + "\nThe node is added as augment from modules with namespaces:\n" + builder, + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); } - if ((schema instanceof ListSchemaNode)) { - final List listKeys = ((ListSchemaNode) schema).getKeyDefinition(); - for (final QName listKey : listKeys) { - boolean foundKey = false; - for (final NodeWrapper child : children) { - if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) { - foundKey = true; - } - } - - if (!foundKey) { - throw new RestconfDocumentedException( - "Missing key in URI \"" + listKey.getLocalName() + - "\" of list \"" + schema.getQName().getLocalName() + "\"", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + boolean rightNodeSchemaFound = false; + for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) { + if (!rightNodeSchemaFound) { + final QName potentialCurrentAugment = + this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint); + if (child.getQname() != null ) { + this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint); + rightNodeSchemaFound = true; } } } - } - else { - if ((nodeBuilder instanceof SimpleNodeWrapper)) { - final SimpleNodeWrapper simpleNode = ((SimpleNodeWrapper) nodeBuilder); - final Object value = simpleNode.getValue(); - Object inputValue = value; - TypeDefinition typeDefinition = this.typeDefinition(schema); - if ((typeDefinition instanceof IdentityrefTypeDefinition)) { - if ((value instanceof String)) { - inputValue = new IdentityValuesDTO( nodeBuilder.getNamespace().toString(), - (String) value, null, (String) value ); - } // else value is already instance of IdentityValuesDTO - } - Codec codec = RestCodec.from(typeDefinition, mountPoint); - Object outputValue = codec == null ? null : codec.deserialize(inputValue); - - simpleNode.setValue(outputValue); + if (!rightNodeSchemaFound) { + throw new RestconfDocumentedException( + "Schema node \"" + child.getLocalName() + "\" was not found in module.", + ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT ); } - else { - if ((nodeBuilder instanceof EmptyNodeWrapper)) { - final EmptyNodeWrapper emptyNodeBuilder = ((EmptyNodeWrapper) nodeBuilder); - if ((schema instanceof LeafSchemaNode)) { - emptyNodeBuilder.setComposite(false); - } - else { - if ((schema instanceof ContainerSchemaNode)) { - // FIXME: Add presence check - emptyNodeBuilder.setComposite(true); - } + } + + if ((schema instanceof ListSchemaNode)) { + ListSchemaNode listSchemaNode = (ListSchemaNode)schema; + final List listKeys = listSchemaNode.getKeyDefinition(); + for (final QName listKey : listKeys) { + boolean foundKey = false; + for (final NodeWrapper child : children) { + if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) { + foundKey = true; } } + + if (!foundKey) { + throw new RestconfDocumentedException( + "Missing key in URI \"" + listKey.getLocalName() + + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE ); + } } } } @@ -1237,6 +1268,9 @@ public class RestconfImpl implements RestconfService { else if (node instanceof LeafSchemaNode) { return _typeDefinition((LeafSchemaNode)node); } + else if (node instanceof AnyXmlSchemaNode) { + return null; + } else { throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.asList(node).toString()); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java index 1ec7253b8d..93d32a1499 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java @@ -116,6 +116,75 @@ public class CnSnToJsonBasicDataTypesTest extends YangAndXmlAndDataSchemaLoader } + static class ComplexAnyXmlVerifier extends LeafVerifier { + + ComplexAnyXmlVerifier() { + super( null, JsonToken.BEGIN_OBJECT ); + } + + @Override + void verify( JsonReader reader, String keyName ) throws IOException { + + reader.beginObject(); + String innerKey = reader.nextName(); + assertEquals( "Json reader child key for " + keyName, "data", innerKey ); + assertEquals( "Json token type for key " + innerKey, JsonToken.BEGIN_OBJECT, reader.peek() ); + + reader.beginObject(); + verifyLeaf( reader, innerKey, "leaf1", "leaf1-value" ); + verifyLeaf( reader, innerKey, "leaf2", "leaf2-value" ); + + String nextName = reader.nextName(); + assertEquals( "Json reader child key for " + innerKey, "leaf-list", nextName ); + reader.beginArray(); + assertEquals( "Json value for key " + nextName, "leaf-list-value1", reader.nextString() ); + assertEquals( "Json value for key " + nextName, "leaf-list-value2", reader.nextString() ); + reader.endArray(); + + nextName = reader.nextName(); + assertEquals( "Json reader child key for " + innerKey, "list", nextName ); + reader.beginArray(); + verifyNestedLists( reader, 1 ); + verifyNestedLists( reader, 3 ); + reader.endArray(); + + reader.endObject(); + reader.endObject(); + } + + void verifyNestedLists( JsonReader reader, int leafNum ) throws IOException { + reader.beginObject(); + + String nextName = reader.nextName(); + assertEquals( "Json reader next name", "nested-list", nextName ); + + reader.beginArray(); + + reader.beginObject(); + verifyLeaf( reader, "nested-list", "nested-leaf", "nested-value" + leafNum++ ); + reader.endObject(); + + reader.beginObject(); + verifyLeaf( reader, "nested-list", "nested-leaf", "nested-value" + leafNum ); + reader.endObject(); + + reader.endArray(); + reader.endObject(); + } + + void verifyLeaf( JsonReader reader, String parent, String name, String value ) throws IOException { + String nextName = reader.nextName(); + assertEquals( "Json reader child key for " + parent, name, nextName ); + assertEquals( "Json token type for key " + parent, JsonToken.STRING, reader.peek() ); + assertEquals( "Json value for key " + nextName, value, reader.nextString() ); + } + + @Override + Object getActualValue( JsonReader reader ) throws IOException { + return null; + } + } + @BeforeClass public static void initialize() { dataLoad("/cnsn-to-json/simple-data-types"); @@ -208,6 +277,9 @@ public class CnSnToJsonBasicDataTypesTest extends YangAndXmlAndDataSchemaLoader expectedMap.put( "lfunion14", new StringVerifier( "zero" ) ); expectedMap.put( "lfempty", new EmptyVerifier() ); expectedMap.put( "identityref1", new StringVerifier( "simple-data-types:iden" ) ); + expectedMap.put( "complex-any", new ComplexAnyXmlVerifier() ); + expectedMap.put( "simple-any", new StringVerifier( "simple" ) ); + expectedMap.put( "empty-any", new StringVerifier( "" ) ); while (jReader.hasNext()) { String keyName = jReader.nextName(); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java index e146cf8f4f..c5c4596189 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.sal.restconf.impl.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -18,6 +19,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.io.ByteStreams; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -29,7 +36,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; - +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -41,7 +49,6 @@ import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; - import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Before; @@ -63,13 +70,6 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.google.common.io.ByteStreams; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; - /** * Unit tests for RestconfDocumentedExceptionMapper. * @@ -842,6 +842,14 @@ public class RestconfDocumentedExceptionMapperTest extends JerseyTest { errorListEntrySet.iterator().next().getKey() ); assertTrue( "\"error\" Json element is not an Array", errorListElement.isJsonArray() ); + // As a final check, make sure there aren't multiple "error" array elements. Unfortunately, + // the call above to getAsJsonObject().entrySet() will out duplicate "error" elements. So + // we'll use regex on the json string to verify this. + + Matcher matcher = Pattern.compile( "\"error\"[ ]*:[ ]*\\[", Pattern.DOTALL ).matcher( bos.toString() ); + assertTrue( "Expected 1 \"error\" element", matcher.find() ); + assertFalse( "Found multiple \"error\" elements", matcher.find() ); + return errorListElement.getAsJsonArray(); } @@ -850,7 +858,6 @@ public class RestconfDocumentedExceptionMapperTest extends JerseyTest { final ErrorInfoVerifier errorInfoVerifier ) { JsonElement errorInfoElement = null; - Map actualErrorInfo = null; Map leafMap = Maps.newHashMap(); for( Entry entry: errorEntryElement.getAsJsonObject().entrySet() ) { String leafName = entry.getKey(); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/structures/LstItem.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/structures/LstItem.java index 9b382d210e..3617ed9fb0 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/structures/LstItem.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/structures/LstItem.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.sal.restconf.impl.test.structures; +import static org.junit.Assert.assertFalse; import java.util.HashMap; import java.util.Map; @@ -56,10 +57,14 @@ public class LstItem { } public void addLfLst(LfLst lfLst) { + assertFalse( "Found multiple leaf list elements for " + lfLst.getName(), + lfLsts.containsKey( lfLst.getName() ) ); lfLsts.put(lfLst.getName(), lfLst); } public void addLst(Lst lst) { + assertFalse( "Found multiple list elements for " + lst.getName(), + lsts.containsKey( lst.getName() ) ); lsts.put(lst.getName(), lst); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/simple-data-types.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/simple-data-types.yang index 9bdea81579..cf6e513b30 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/simple-data-types.yang +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/simple-data-types.yang @@ -269,6 +269,10 @@ module simple-data-types { } } - + anyxml complex-any; + + anyxml simple-any; + + anyxml empty-any; } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml index 28d355dbab..ed02ca35f6 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/cnsn-to-json/simple-data-types/xml/data.xml @@ -23,7 +23,6 @@ enum3 bit3 bit2 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz - 324 33.3 55 @@ -38,5 +37,35 @@ false b1 zero + x:iden - \ No newline at end of file + + + leaf1-value + leaf2-value + + leaf-list-value1 + leaf-list-value2 + + + + nested-value1 + + + nested-value2 + + + + + + nested-value3 + + + nested-value4 + + + + + simple + +