diff options
author | Jan Srnicek <jsrnicek@cisco.com> | 2017-02-10 08:55:55 +0100 |
---|---|---|
committer | Marek Gradzki <mgradzki@cisco.com> | 2017-02-10 08:53:27 +0000 |
commit | de55d1e7c1fa5517ee6eabcd3fa23e5b5136d64b (patch) | |
tree | 057cd5f38c18da91852c2d79168ae41c0c6bfed6 | |
parent | 8450b69800c827ba221351eb0c374fcbd9146593 (diff) |
HONEYCOMB-334 - List entry injection for yang data
Major changes
- mechanism to inject list entries by key
- provided processor registry to hide explicit implementations
Minor changes
- general refactoring
Test cases
- list in root of model
- list under container
- list under nested container
- list in augmentation
Change-Id: I9abe1ce5f9176c132ad88627b135516574e40e06
Signed-off-by: Jan Srnicek <jsrnicek@cisco.com>
22 files changed, 806 insertions, 304 deletions
diff --git a/infra/test-utils/test-api/src/main/yang/hc-data.yang b/infra/test-utils/test-api/src/main/yang/hc-data.yang index a817a249f..6cd524565 100644 --- a/infra/test-utils/test-api/src/main/yang/hc-data.yang +++ b/infra/test-utils/test-api/src/main/yang/hc-data.yang @@ -12,6 +12,13 @@ module hc-data{ prefix "ext"; } + list root-list { + key root-name; + leaf root-name{ + type string; + } + } + container simple-container{ list simple-list{ key name; @@ -23,6 +30,13 @@ module hc-data{ type string; } } + + list nested-list { + key nested-name; + leaf nested-name { + type string; + } + } } container nested-container{ @@ -67,5 +81,12 @@ module hc-data{ leaf name-in-augment{ type string; } + + list list-in-augment{ + key key-in-augment; + leaf key-in-augment{ + type string; + } + } } }
\ No newline at end of file diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java new file mode 100644 index 000000000..93360d25a --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/AbstractYangContextHolder.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +import javax.annotation.Nonnull; + +abstract class AbstractYangContextHolder { + + private final SchemaContext schemaContext; + private final BindingToNormalizedNodeCodec serializer; + + AbstractYangContextHolder(@Nonnull final SchemaContext schemaContext, + @Nonnull final BindingToNormalizedNodeCodec serializer){ + this.schemaContext=schemaContext; + this.serializer=serializer; + } + + SchemaContext schemaContext() { + return schemaContext; + } + + BindingToNormalizedNodeCodec serializer() { + return serializer; + } +} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java new file mode 100644 index 000000000..089bb43cd --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessor.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.io.InputStream; + +import static com.google.common.base.Preconditions.checkState; +import static io.fd.honeycomb.translate.util.JsonUtils.readContainerEntryJson; +import static io.fd.honeycomb.translate.util.JsonUtils.readJson; + +final class ContainerNodeDataProcessor extends AbstractYangContextHolder implements YangDataProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(ContainerNodeDataProcessor.class); + + ContainerNodeDataProcessor(@Nonnull SchemaContext schemaContext, @Nonnull BindingToNormalizedNodeCodec serializer) { + super(schemaContext, serializer); + } + + @Nonnull + @Override + public DataObject getNodeData(@Nonnull YangInstanceIdentifier yangInstanceIdentifier, @Nonnull String resourcePath) { + + final InputStream resourceStream = this.getClass().getResourceAsStream(resourcePath); + final YangInstanceIdentifier nodeParent = getNodeParent(yangInstanceIdentifier).orElse(null); + final SchemaNode parentSchema = parentSchema(schemaContext(), serializer(), nodeParent, () -> LOG); + + // to be able to process containers in root of model + if (isRoot(yangInstanceIdentifier)) { + // if root ,read as root + final ContainerNode data = readJson(schemaContext(), resourceStream, parentSchema); + checkState(data.getValue().size() == 1, "Single root expected in %s", resourcePath); + //then extracts first child + final QName rootNodeType = data.getValue().iterator().next().getNodeType(); + final YangInstanceIdentifier realIdentifier = YangInstanceIdentifier.of(rootNodeType); + return nodeBinding(serializer(), realIdentifier, data).getValue(); + } else { + // reads just container + final YangInstanceIdentifier.NodeIdentifier nodeIdentifier = containerNodeIdentifier(yangInstanceIdentifier); + final ContainerNode data = readContainerEntryJson(schemaContext(), resourceStream, parentSchema, nodeIdentifier); + return nodeBinding(serializer(), yangInstanceIdentifier, data.getValue().iterator().next()).getValue(); + } + } + + @Override + public boolean canProcess(@Nonnull YangInstanceIdentifier identifier) { + return isRoot(identifier) || + isContainer(identifierBinding(serializer(), identifier).getTargetType()); + } + + private static boolean isContainer(final Class targetType) { + return !Identifiable.class.isAssignableFrom(targetType) + && !Augmentation.class.isAssignableFrom(targetType); + } + + private static YangInstanceIdentifier.NodeIdentifier containerNodeIdentifier(@Nonnull final YangInstanceIdentifier nodeIdentifier) { + return java.util.Optional.ofNullable(nodeIdentifier.getLastPathArgument()) + .filter(pathArgument -> pathArgument instanceof YangInstanceIdentifier.NodeIdentifier) + .map(YangInstanceIdentifier.NodeIdentifier.class::cast) + .orElseThrow(() -> new IllegalArgumentException( + String.format("Unable to create container node identifier from %s", nodeIdentifier))); + } +} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java index 906a5fc87..0f8b1b9af 100644 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java @@ -17,29 +17,23 @@ package io.fd.honeycomb.test.tools; import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor; -import io.fd.honeycomb.test.tools.factories.ChildNodeDataFactory; -import io.fd.honeycomb.test.tools.factories.RootNodeDataFactory; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Parameter; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import javax.annotation.Nullable; -import org.apache.commons.lang3.StringUtils; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.annotation.Annotation; +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangContextProducer, InjectablesProcessor { private static final Logger LOG = LoggerFactory.getLogger(HoneycombTestRunner.class); @@ -48,8 +42,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC private BindingToNormalizedNodeCodec serializer; private AbstractModuleStringInstanceIdentifierCodec iidParser; - private ChildNodeDataFactory childNodeDataFactory; - private RootNodeDataFactory rootNodeDataFactory; + private YangDataProcessorRegistry processorRegistry; public HoneycombTestRunner(final Class<?> klass) throws InitializationError { super(klass); @@ -64,12 +57,11 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC final ModuleInfoBackedContext ctx = getCheckedModuleInfoContext(test); schemaContext = ctx.getSchemaContext(); // Create serializer from it in order to later transform NormalizedNodes into BA - serializer = createSerializer(ctx, schemaContext); + serializer = createSerializer(ctx); // Create InstanceIdentifier Codec in order to later transform string represented IID into YangInstanceIdentifier iidParser = getIIDCodec(ctx); - childNodeDataFactory = new ChildNodeDataFactory(schemaContext, serializer, iidParser); - rootNodeDataFactory = new RootNodeDataFactory(schemaContext, serializer, iidParser); + processorRegistry = YangDataProcessorRegistry.create(schemaContext, serializer); injectFields(test); return test; @@ -83,7 +75,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC /** * Allows method parameters injection - * */ + */ @Override protected Statement methodInvoker(final FrameworkMethod method, final Object test) { return new InjectableTestMethodInvoker(method, test, Arrays.stream(method.getMethod().getParameters()) @@ -94,7 +86,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC private Object injectValueOrNull(final Parameter parameter) { return isInjectable(parameter) - ? getData(resourcePath(parameter), instanceIdentifier(parameter).orNull(), parameter.getType()) + ? processorRegistry.getNodeData(instanceIdentifier(iidParser, parameter), resourcePath(parameter)) : null; } @@ -107,22 +99,7 @@ public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangC injectableFields(testInstance.getClass()).forEach(field -> { LOG.debug("Processing field {}", field); injectField(field, testInstance, - getData(resourcePath(field), instanceIdentifier(field).orNull(), field.getType())); + processorRegistry.getNodeData(instanceIdentifier(iidParser, field), resourcePath(field))); }); } - - private DataObject getData(final String resourcePath, @Nullable final String identifier, - final Class<?> injectedType) { - try { - if (StringUtils.isNotEmpty(identifier)) { - LOG.debug("Processing {} as child node {}", injectedType, identifier); - return childNodeDataFactory.getChildNodeData(identifier, resourcePath); - } else { - LOG.debug("Processing {} as root node", injectedType); - return rootNodeDataFactory.getRootNodeData(getRootInstanceIdentifier(injectedType), resourcePath); - } - } catch (DeserializationException | IOException e) { - throw new IllegalStateException(e); - } - } } diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java new file mode 100644 index 000000000..375f55307 --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/ListNodeDataProcessor.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.io.InputStream; + +import static com.google.common.base.Preconditions.checkArgument; +import static io.fd.honeycomb.translate.util.JsonUtils.readListEntryFromJson; + +/** + * json --> BA processor for list entry data + */ +final class ListNodeDataProcessor extends AbstractYangContextHolder implements YangDataProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(ListNodeDataProcessor.class); + + ListNodeDataProcessor(@Nonnull final SchemaContext schemaContext, + @Nonnull final BindingToNormalizedNodeCodec serializer) { + super(schemaContext, serializer); + } + + @Nonnull + @Override + public DataObject getNodeData(@Nonnull final YangInstanceIdentifier nodeIdentifier, + @Nonnull final String resourcePath) { + checkArgument(canProcess(nodeIdentifier), "Cannot process identifier %s", nodeIdentifier); + final YangInstanceIdentifier listParent = listNodeParent(nodeIdentifier); + final YangInstanceIdentifier.NodeIdentifierWithPredicates keyedNodeIdentifier = listNodeIdentifier(nodeIdentifier); + final InputStream resourceStream = this.getClass().getResourceAsStream(resourcePath); + final SchemaNode parentSchemaNode = parentSchema(schemaContext(), serializer(), listParent, () -> LOG); + final MapEntryNode data = readListEntryFromJson(schemaContext(), resourceStream, parentSchemaNode, keyedNodeIdentifier); + + return nodeBinding(serializer(), nodeIdentifier, data).getValue(); + } + + @Override + public boolean canProcess(@Nonnull final YangInstanceIdentifier identifier) { + return !isRoot(identifier) && + Identifiable.class.isAssignableFrom(identifierBinding(serializer(), identifier).getTargetType()); + } + + private YangInstanceIdentifier listNodeParent(@Nonnull final YangInstanceIdentifier nodeIdentifier) { + // if it is list, real parent is second to last + return getNodeParent(nodeIdentifier).map(parent -> getNodeParent(parent).orElse(null)) + .orElseThrow(() -> new IllegalArgumentException( + String.format("Unable to get parent for list node from %s", nodeIdentifier))); + } + + private static YangInstanceIdentifier.NodeIdentifierWithPredicates listNodeIdentifier(@Nonnull final YangInstanceIdentifier nodeIdentifier) { + return java.util.Optional.ofNullable(nodeIdentifier.getLastPathArgument()) + .filter(pathArgument -> pathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) + .map(YangInstanceIdentifier.NodeIdentifierWithPredicates.class::cast) + .orElseThrow(() -> new IllegalArgumentException( + String.format("Unable to create list node identifier from %s", nodeIdentifier))); + } +} + + + diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java index 23af9733d..017a68c9f 100644 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java @@ -16,12 +16,7 @@ package io.fd.honeycomb.test.tools; -import static com.google.common.base.Preconditions.checkState; - import io.fd.honeycomb.test.tools.annotations.SchemaContextProvider; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import javassist.ClassPool; import org.apache.commons.lang3.reflect.MethodUtils; import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; @@ -34,6 +29,12 @@ import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory; import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static com.google.common.base.Preconditions.checkState; + /** * Common logic to initialize serializers/deserializers/etc while working with yang based data */ @@ -67,14 +68,13 @@ interface YangContextProducer { return (AbstractModuleStringInstanceIdentifierCodec) cstr.newInstance(ctx.getSchemaContext(), jsonCodecFactory); } - default BindingToNormalizedNodeCodec createSerializer(final ModuleInfoBackedContext moduleInfoBackedContext, - final SchemaContext schemaContexts) { + default BindingToNormalizedNodeCodec createSerializer(final ModuleInfoBackedContext moduleInfoBackedContext) { final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry( StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault()))); codecRegistry - .onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, schemaContexts)); + .onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, moduleInfoBackedContext.getSchemaContext())); return new BindingToNormalizedNodeCodec(moduleInfoBackedContext, codecRegistry); } } diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java new file mode 100644 index 000000000..a353e4b3b --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessor.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.slf4j.Logger; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.AbstractMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +interface YangDataProcessor { + + /** + * Attempts to find data in file specified by <b>resourcePath</b>,<br> + * and translate it to BA object + * + * @param yangInstanceIdentifier identifier of path to read + * @param resourcePath path of resource file to load + */ + @Nonnull + DataObject getNodeData(@Nonnull final YangInstanceIdentifier yangInstanceIdentifier, + @Nonnull final String resourcePath); + + /** + * Verifies if provided identifier is identifying node processed by this processor + * + * @param identifier node identifier + */ + boolean canProcess(@Nonnull final YangInstanceIdentifier identifier); + + default boolean isRoot(@Nonnull final YangInstanceIdentifier identifier) { + return identifier.getPathArguments().isEmpty(); + } + + @Nonnull + default Optional<YangInstanceIdentifier> getNodeParent(@Nonnull final YangInstanceIdentifier identifier) { + return Optional.ofNullable(identifier.getParent()); + } + + @Nonnull + default SchemaNode parentSchema(@Nonnull final SchemaContext schemaContext, + @Nonnull final BindingToNormalizedNodeCodec serializer, + @Nullable final YangInstanceIdentifier parentYangId, + @Nonnull final Supplier<Logger> logProvider) { + // null or root + if (parentYangId == null || parentYangId.getPathArguments().size() == 0) { + // no parent == use schema context as root context + logProvider.get().info("Parent is null, providing schema context as parent node"); + return schemaContext; + } + + final com.google.common.base.Optional<InstanceIdentifier<? extends DataObject>> parentInstanceId; + try { + parentInstanceId = serializer.toBinding(parentYangId); + } catch (DeserializationException e) { + throw new IllegalArgumentException(String.format("Unable to deserialize %s", parentYangId)); + } + + if (!parentInstanceId.isPresent()) { + throw new IllegalStateException(String.format("Unable to resolve %s to instance identifier", parentYangId)); + } + + final com.google.common.base.Optional<DataNodeContainer> dataNodeContainerOptional = + BindingSchemaContextUtils.findDataNodeContainer(schemaContext, parentInstanceId.get()); + + + if (!dataNodeContainerOptional.isPresent()) { + throw new IllegalArgumentException(String.format("Error finding DataNodeContainer for %s", parentInstanceId.get())); + } + + final DataNodeContainer parentNode = dataNodeContainerOptional.get(); + logProvider.get().info("Parent schema node resolved as {}", parentNode); + return (SchemaNode) parentNode; + } + + @Nonnull + default Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> nodeBinding(@Nonnull final BindingToNormalizedNodeCodec serializer, + @Nonnull final YangInstanceIdentifier identifier, + @Nonnull final NormalizedNode<?, ?> data) { + try { + return serializer.toBinding(new AbstractMap.SimpleImmutableEntry<>(identifier, data)) + .or(() -> { + throw new IllegalArgumentException(String.format("Unable to create node binding for %s|%s", identifier, data)); + }); + } catch (DeserializationException e) { + throw new IllegalArgumentException(String.format("Unable to deserialize node %s|%s", identifier, data)); + } + } + + @Nonnull + default InstanceIdentifier<? extends DataObject> identifierBinding(@Nonnull final BindingToNormalizedNodeCodec serializer, + @Nonnull final YangInstanceIdentifier identifier) { + try { + return serializer.toBinding(identifier) + .or(() -> { + throw new IllegalArgumentException(String.format("Unable convert %s to binding", identifier)); + }); + } catch (DeserializationException e) { + throw new IllegalArgumentException(String.format("Unable to deserialize %s", identifier)); + } + } +} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java new file mode 100644 index 000000000..d23cd59c1 --- /dev/null +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangDataProcessorRegistry.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +import javax.annotation.Nonnull; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Process yang data from json to BA Objects + */ +final class YangDataProcessorRegistry { + + private final List<YangDataProcessor> processors; + + private YangDataProcessorRegistry(@Nonnull final SchemaContext context, + @Nonnull final BindingToNormalizedNodeCodec codec) { + // linked should be faster for iteration + processors = new LinkedList<>(); + processors.add(new ListNodeDataProcessor(context, codec)); + processors.add(new ContainerNodeDataProcessor(context, codec)); + } + + static YangDataProcessorRegistry create(@Nonnull final SchemaContext context, + @Nonnull final BindingToNormalizedNodeCodec codec) { + return new YangDataProcessorRegistry(context, codec); + } + + @Nonnull + DataObject getNodeData(@Nonnull final YangInstanceIdentifier yangInstanceIdentifier, + @Nonnull final String resourcePath) { + return pickProcessor(yangInstanceIdentifier).getNodeData(yangInstanceIdentifier, resourcePath); + } + + private YangDataProcessor pickProcessor(final YangInstanceIdentifier yangInstanceIdentifier) { + final List<YangDataProcessor> eligibleProcessors = processors.stream() + .filter(processors -> processors.canProcess(yangInstanceIdentifier)) + .collect(Collectors.toList()); + + // canProcess should be exclusive for node type, but just in case + checkState(eligibleProcessors.size() == 1, + "No single eligible processor found, matches=[%s]", eligibleProcessors); + + return eligibleProcessors.get(0); + } +} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java index c364938ed..216d8353b 100644 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java +++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java @@ -16,22 +16,21 @@ package io.fd.honeycomb.test.tools.annotations; -import static io.fd.honeycomb.test.tools.annotations.InjectTestData.NO_ID; - -import com.google.common.base.Optional; -import java.lang.reflect.Field; -import java.lang.reflect.Parameter; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; import org.apache.commons.lang3.reflect.FieldUtils; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.YangModuleInfo; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; + +import javax.annotation.Nonnull; +import java.lang.reflect.Field; +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.Set; + +import static io.fd.honeycomb.test.tools.annotations.InjectTestData.NO_ID; /** * Common logic for @InjectTestData @@ -54,23 +53,23 @@ public interface InjectablesProcessor { return parameter.getAnnotation(InjectTestData.class).resourcePath(); } - default Optional<String> instanceIdentifier(final Field field) { + default YangInstanceIdentifier instanceIdentifier(@Nonnull final AbstractModuleStringInstanceIdentifierCodec parser, @Nonnull final Field field) { final String identifier = field.getAnnotation(InjectTestData.class).id(); // == used instead of equals to ensure constant was used if (NO_ID.equals(identifier)) { - return Optional.absent(); + return getRootInstanceIdentifier(field.getType()); } else { - return Optional.of(identifier); + return parser.deserialize(identifier); } } - default Optional<String> instanceIdentifier(final Parameter parameter) { + default YangInstanceIdentifier instanceIdentifier(@Nonnull final AbstractModuleStringInstanceIdentifierCodec parser, @Nonnull final Parameter parameter) { final String identifier = parameter.getAnnotation(InjectTestData.class).id(); // == used instead of equals to ensure constant was used if (NO_ID.equals(identifier)) { - return Optional.absent(); + return getRootInstanceIdentifier(parameter.getType()); } else { - return Optional.of(identifier); + return parser.deserialize(identifier); } } @@ -83,7 +82,7 @@ public interface InjectablesProcessor { } } - default YangInstanceIdentifier getRootInstanceIdentifier(final Class type) { + static YangInstanceIdentifier getRootInstanceIdentifier(final Class type) { try { return YangInstanceIdentifier.of(QName.class.cast(type.getField("QNAME").get(null))); } catch (IllegalAccessException e) { diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java deleted file mode 100644 index 05812123e..000000000 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * 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. - */ - -package io.fd.honeycomb.test.tools.factories; - - -import com.google.common.base.Optional; -import java.io.IOException; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; -import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ChildNodeDataFactory extends YangDataFactory { - - private static final Logger LOG = LoggerFactory.getLogger(ChildNodeDataFactory.class); - - public ChildNodeDataFactory(@Nonnull final SchemaContext schemaContext, - @Nonnull final BindingToNormalizedNodeCodec serializer, - @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) { - super(schemaContext, serializer, iidParser); - } - - public DataObject getChildNodeData(final String instanceIdentifier, - final String resourcePath) throws DeserializationException, IOException { - // Parse string ID into YangId - final YangInstanceIdentifier nodeYid = iidParser.deserialize(instanceIdentifier); - // Look for parent YangId - final YangInstanceIdentifier parentYid = nodeYid.getParent(); - - if (parentYid.isEmpty()) { - throw new IllegalArgumentException( - "Attempt to process root node as children has been detected,to process root nodes just don't use id in @InjectTestData"); - } - // Find Schema node for parent of data that's currently being parsed (needed when parsing the data into NormalizedNodes) - return getDataForNode(nodeYid, resourcePath, getNonRootParentSchema(parentYid)); - } - - private DataNodeContainer getNonRootParentSchema(final YangInstanceIdentifier parentYangId) - throws DeserializationException { - LOG.debug("Processing parent identifier {}", parentYangId); - final Optional<InstanceIdentifier<? extends DataObject>> parentInstanceId = serializer.toBinding(parentYangId); - if (!parentInstanceId.isPresent()) { - throw new IllegalStateException("Unable to resolve " + parentYangId + " to instance identifier"); - } - - final Optional<DataNodeContainer> dataNodeContainerOptional = - BindingSchemaContextUtils.findDataNodeContainer(schemaContext, parentInstanceId.get()); - - if (!dataNodeContainerOptional.isPresent()) { - throw new IllegalArgumentException("Error finding DataNodeContainer for " + parentInstanceId.get()); - } - - return dataNodeContainerOptional.get(); - } -} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java deleted file mode 100644 index 3aacea016..000000000 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * 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. - */ - -package io.fd.honeycomb.test.tools.factories; - - -import java.io.IOException; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; -import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; - -public class RootNodeDataFactory extends YangDataFactory { - - public RootNodeDataFactory(@Nonnull final SchemaContext schemaContext, - @Nonnull final BindingToNormalizedNodeCodec serializer, - @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) { - super(schemaContext, serializer, iidParser); - } - - public DataObject getRootNodeData(final YangInstanceIdentifier rootInstanceIdentifier, - final String resourcePath) throws DeserializationException, IOException { - //entire schema context is parent schema in this case - return getDataForNode(rootInstanceIdentifier, resourcePath, schemaContext); - } -} diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java deleted file mode 100644 index ecf556cd2..000000000 --- a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * 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. - */ - -package io.fd.honeycomb.test.tools.factories; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.base.Optional; -import com.google.common.collect.Iterables; -import io.fd.honeycomb.translate.util.JsonUtils; -import java.io.IOException; -import java.util.AbstractMap; -import java.util.Map; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; -import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; - -/** - * Common logic for reading of yang data - */ -abstract class YangDataFactory { - - final SchemaContext schemaContext; - final BindingToNormalizedNodeCodec serializer; - final AbstractModuleStringInstanceIdentifierCodec iidParser; - - YangDataFactory(@Nonnull final SchemaContext schemaContext, - @Nonnull final BindingToNormalizedNodeCodec serializer, - @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) { - this.schemaContext = checkNotNull(schemaContext, "SchemaContext cannot be null"); - this.serializer = checkNotNull(serializer, "Serializer cannot be null"); - this.iidParser = checkNotNull(iidParser, "Instance identifier parser cannot be null"); - } - - DataObject getDataForNode(final YangInstanceIdentifier nodeYangIdentifier, - final String resourcePath, - final DataNodeContainer parentSchema) - throws DeserializationException, IOException { - - // Reads resources from provided resource path - final ContainerNode rootData = getCheckedRootData(resourcePath, parentSchema); - - // Now transform the single child from JSON into BA format - final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData = - extractCheckedSingleChild(rootData); - - return getCheckedBinding(nodeYangIdentifier, actualData).getValue(); - } - - private ContainerNode getCheckedRootData(final String resourcePath, final DataNodeContainer parentSchema) - throws IOException { - // TODO the cast to SchemaNode is dangerous and would not work for Augments, Choices and some other nodes maybe. At least check - // TODO not sure if this is true, while testing this code was working fine event while processing choices/cases, - // TODO only problem is to find suitable codec that can process cases,etc - // Transform JSON into NormalizedNode - - final ContainerNode rootData = JsonUtils.readJson(schemaContext, - checkNotNull(this.getClass().getResource(resourcePath), "Unable to find resource %s", resourcePath) - .openStream(), ((SchemaNode) parentSchema)); - - checkArgument(rootData.getValue().size() == 1, "Only a single data node is expected in %s, but there were: %s", - resourcePath, rootData.getValue()); - return rootData; - } - - private DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> extractCheckedSingleChild( - final ContainerNode rootData) { - // Now transform the single child from JSON into BA format - final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData = - Iterables.getFirst(rootData.getValue(), null); - - checkNotNull(actualData, "Unable to extract single child from %s", rootData); - return actualData; - } - - private Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> getCheckedBinding( - final YangInstanceIdentifier nodeYangIdentifier, - final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData) - throws DeserializationException { - final Optional<Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject>> ba = - serializer.toBinding(new AbstractMap.SimpleImmutableEntry<>(nodeYangIdentifier, actualData)); - - checkArgument(ba.isPresent(), "Unable to convert to binding %s", nodeYangIdentifier); - return ba.get(); - } -} diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/AbstractYangDataProcessorTest.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/AbstractYangDataProcessorTest.java new file mode 100644 index 000000000..2d5f0259d --- /dev/null +++ b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/AbstractYangDataProcessorTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + + +import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor; +import org.junit.Before; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; + +abstract class AbstractYangDataProcessorTest implements InjectablesProcessor, YangContextProducer { + + ModuleInfoBackedContext moduleInfoBackedContext; + AbstractModuleStringInstanceIdentifierCodec codec; + BindingToNormalizedNodeCodec serializer; + + @Before + public void init() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + moduleInfoBackedContext = provideSchemaContextFor(Collections.singleton($YangModuleInfoImpl.getInstance())); + codec = getIIDCodec(moduleInfoBackedContext); + serializer = createSerializer(moduleInfoBackedContext); + + // to init children + setUp(); + } + + // for children init + abstract void setUp(); +} diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessorTest.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessorTest.java new file mode 100644 index 000000000..e1dbc1803 --- /dev/null +++ b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ContainerNodeDataProcessorTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + + +import org.junit.Test; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +import static org.junit.Assert.*; + +public class ContainerNodeDataProcessorTest extends AbstractYangDataProcessorTest { + + private ContainerNodeDataProcessor processor; + + @Override + void setUp() { + processor = new ContainerNodeDataProcessor(moduleInfoBackedContext.getSchemaContext(), serializer); + } + + @Test + public void testGetNodeDataNestedContainer() { + final DataObject nodeData = processor.getNodeData(codec.deserialize(InjectionTestData.CONTAINER_UNDER_LIST_DATA_PATH), + InjectionTestData.CONTAINER_UNDER_LIST_RESOURCE); + assertNotNull(nodeData); + } + + @Test + public void testGetNodeDataRootContainer() { + final DataObject nodeData = processor.getNodeData(YangInstanceIdentifier.EMPTY, InjectionTestData.CONTAINER_IN_ROOT_RESOURCE); + assertNotNull(nodeData); + } + + + @Test + public void testCanProcessNegative() { + assertFalse(processor.canProcess(codec.deserialize(InjectionTestData.SIMPLE_LIST_DATA_PATH))); + assertFalse(processor.canProcess(codec.deserialize(InjectionTestData.NESTED_LIST_DATA_PATH))); + } + + @Test + public void testCanProcessPositive() { + assertTrue(processor.canProcess(YangInstanceIdentifier.EMPTY)); + assertTrue(processor.canProcess(codec.deserialize(InjectionTestData.CONTAINER_UNDER_LIST_DATA_PATH))); + } +} diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerTest.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerContainerTest.java index 42de7ed4d..1461fa377 100644 --- a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerTest.java +++ b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerContainerTest.java @@ -16,27 +16,27 @@ package io.fd.honeycomb.test.tools; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - import io.fd.honeycomb.test.tools.annotations.InjectTestData; import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor; import io.fd.honeycomb.test.tools.annotations.SchemaContextProvider; -import java.util.Collections; import org.junit.Test; import org.junit.runner.RunWith; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.AugContainerAugmentation; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.SimpleContainer; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.SimpleContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.*; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.NestedContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.SimpleList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.augmented.container.ListInAugment; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.ContUnderList; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.ContUnderListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.NestedList; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import java.util.Collections; + +import static io.fd.honeycomb.test.tools.InjectionTestData.*; +import static org.junit.Assert.*; + @RunWith(HoneycombTestRunner.class) -public class HoneycombTestRunnerTest implements InjectablesProcessor { +public class HoneycombTestRunnerContainerTest implements InjectablesProcessor { @InjectTestData(resourcePath = "/simpleContainerEmpty.json") private SimpleContainer simpleContainer; @@ -52,6 +52,19 @@ public class HoneycombTestRunnerTest implements InjectablesProcessor { "/hc-data:cont-under-list") private ContUnderList containerUnderList; + @InjectTestData(resourcePath = AUGMENT_LIST_RESOURCE, id = AUGMENT_LIST_DATA_PATH) + private ListInAugment listInAugment; + + @InjectTestData(resourcePath = NESTED_LIST_RESOURCE, id = NESTED_LIST_DATA_PATH) + private NestedList nestedList; + + @InjectTestData(resourcePath = ROOT_LIST_RESOURCE, id = ROOT_LIST_DATA_PATH) + private RootList rootList; + + @InjectTestData(resourcePath = SIMPLES_LIST_RESOURCE, id = SIMPLE_LIST_DATA_PATH) + private SimpleList simpleList; + + @SchemaContextProvider public ModuleInfoBackedContext getSchemaContext() { return provideSchemaContextFor(Collections.singleton($YangModuleInfoImpl.getInstance())); diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/InjectionTestData.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/InjectionTestData.java new file mode 100644 index 000000000..4d4ab5420 --- /dev/null +++ b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/InjectionTestData.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + +final class InjectionTestData { + + static final String CONTAINER_UNDER_LIST_DATA_PATH = "/hc-data:simple-container" + + "/hc-data:simple-list[hc-data:name='nameUnderSimpleList']" + + "/hc-data:cont-under-list"; + + static final String SIMPLE_LIST_DATA_PATH = "/hc-data:simple-container" + + "/hc-data:simple-list[hc-data:name='nameUnderSimpleList']"; + + static final String NESTED_LIST_DATA_PATH = "/hc-data:simple-container" + + "/hc-data:simple-list[hc-data:name='nameUnderSimpleList']" + + "/hc-data:nested-list[hc-data:nested-name='nameUnderNestedList']"; + + static final String ROOT_LIST_DATA_PATH = "/hc-data:root-list[hc-data:root-name='rootName']"; + static final String AUGMENT_LIST_DATA_PATH = "/hc-data:simple-container" + + "/hc-data:augmented-container" + + "/hc-data:list-in-augment[hc-data:key-in-augment='keyInAugment']"; + + static final String CONTAINER_IN_ROOT_RESOURCE = "/simpleContainerEmpty.json"; + static final String CONTAINER_UNDER_LIST_RESOURCE = "/containerInList.json"; + static final String SIMPLES_LIST_RESOURCE = "/simpleListEntry.json"; + static final String NESTED_LIST_RESOURCE = "/nestedListEntry.json"; + static final String ROOT_LIST_RESOURCE = "/rootListEntry.json"; + static final String AUGMENT_LIST_RESOURCE = "/augmentListEntry.json"; + + private InjectionTestData() { + } +} diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ListNodeDataProcessorTest.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ListNodeDataProcessorTest.java new file mode 100644 index 000000000..1118d3387 --- /dev/null +++ b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/ListNodeDataProcessorTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * 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. + */ + +package io.fd.honeycomb.test.tools; + + +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.RootList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.SimpleList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.augmented.container.ListInAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.NestedList; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +import static io.fd.honeycomb.test.tools.InjectionTestData.*; +import static org.junit.Assert.*; + +public class ListNodeDataProcessorTest extends AbstractYangDataProcessorTest { + + private ListNodeDataProcessor processor; + + @Override + void setUp() { + processor = new ListNodeDataProcessor(moduleInfoBackedContext.getSchemaContext(), serializer); + } + + @Test + public void testCanProcessPositive() { + assertTrue(processor.canProcess(codec.deserialize(SIMPLE_LIST_DATA_PATH))); + assertTrue(processor.canProcess(codec.deserialize(NESTED_LIST_DATA_PATH))); + } + + @Test + public void testCanProcessNegative() { + assertFalse(processor.canProcess(codec.deserialize(CONTAINER_UNDER_LIST_DATA_PATH))); + assertFalse(processor.canProcess(YangInstanceIdentifier.EMPTY)); + } + + @Test + public void testGetNodeDataSimpleList() { + final DataObject nodeData = processor.getNodeData(codec.deserialize(SIMPLE_LIST_DATA_PATH), SIMPLES_LIST_RESOURCE); + assertNotNull(nodeData); + assertEquals("nameUnderSimpleList", ((SimpleList) nodeData).getName()); + } + + @Test + public void testGetNodeDataNestedList() { + final DataObject nodeData = processor.getNodeData(codec.deserialize(NESTED_LIST_DATA_PATH), NESTED_LIST_RESOURCE); + assertNotNull(nodeData); + assertEquals("nameUnderNestedList", ((NestedList) nodeData).getNestedName()); + } + + @Test + public void testGetNodeDataRootList() { + final DataObject nodeData = processor.getNodeData(codec.deserialize(ROOT_LIST_DATA_PATH), ROOT_LIST_RESOURCE); + assertNotNull(nodeData); + assertEquals("rootName", ((RootList) nodeData).getRootName()); + } + + @Test + public void testGetNodeDataAugmentList() { + final DataObject nodeData = processor.getNodeData(codec.deserialize(AUGMENT_LIST_DATA_PATH), AUGMENT_LIST_RESOURCE); + assertNotNull(nodeData); + assertEquals("keyInAugment", ((ListInAugment) nodeData).getKeyInAugment()); + } +} diff --git a/infra/test-utils/test-tools/src/test/resources/augmentListEntry.json b/infra/test-utils/test-tools/src/test/resources/augmentListEntry.json new file mode 100644 index 000000000..1bdc3e6e2 --- /dev/null +++ b/infra/test-utils/test-tools/src/test/resources/augmentListEntry.json @@ -0,0 +1,10 @@ +{ + "list-in-augment":[ + { + "key-in-augment":"keyInAugment" + }, + { + "key-in-augment":"otherKeyInAugment" + } + ] +}
\ No newline at end of file diff --git a/infra/test-utils/test-tools/src/test/resources/nestedListEntry.json b/infra/test-utils/test-tools/src/test/resources/nestedListEntry.json new file mode 100644 index 000000000..c874f1c94 --- /dev/null +++ b/infra/test-utils/test-tools/src/test/resources/nestedListEntry.json @@ -0,0 +1,10 @@ +{ + "nested-list": [ + { + "nested-name": "nameUnderNestedList" + }, + { + "nested-name": "otherName" + } + ] +} diff --git a/infra/test-utils/test-tools/src/test/resources/rootListEntry.json b/infra/test-utils/test-tools/src/test/resources/rootListEntry.json new file mode 100644 index 000000000..cd09c685e --- /dev/null +++ b/infra/test-utils/test-tools/src/test/resources/rootListEntry.json @@ -0,0 +1,10 @@ +{ + "root-list":[ + { + "root-name":"rootName" + }, + { + "root-name":"otherRootName" + } + ] +}
\ No newline at end of file diff --git a/infra/test-utils/test-tools/src/test/resources/simpleListEntry.json b/infra/test-utils/test-tools/src/test/resources/simpleListEntry.json new file mode 100644 index 000000000..64ad3e6bd --- /dev/null +++ b/infra/test-utils/test-tools/src/test/resources/simpleListEntry.json @@ -0,0 +1,10 @@ +{ + "simple-list": [ + { + "name": "nameUnderSimpleList" + }, + { + "name": "otherName" + } + ] +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java index 3216cac20..332bf6767 100644 --- a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/JsonUtils.java @@ -19,15 +19,10 @@ package io.fd.honeycomb.translate.util; import com.google.common.base.Charsets; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; @@ -44,6 +39,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; +import java.io.*; + public final class JsonUtils { private static final Logger LOG = LoggerFactory.getLogger(JsonUtils.class); @@ -101,6 +99,43 @@ public final class JsonUtils { return builder.build(); } + public static ContainerNode readContainerEntryJson(@Nonnull final SchemaContext schemaContext, + @Nonnull final InputStream stream, + @Nonnull final SchemaNode parentSchema, + @Nonnull final YangInstanceIdentifier.NodeIdentifier nodeIdentifier) { + final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> builder = + Builders.containerBuilder().withNodeIdentifier(nodeIdentifier); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(builder); + + try (final JsonParserStream jsonParser = JsonParserStream.create(writer, schemaContext, parentSchema)) { + final JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8)); + jsonParser.parse(reader); + } catch (IOException e) { + LOG.warn("Unable to close json parser. Ignoring exception", e); + } + + return builder.build(); + } + + public static MapEntryNode readListEntryFromJson(@Nonnull final SchemaContext schemaContext, + @Nonnull final InputStream stream, + @Nonnull final SchemaNode parentSchema, + @Nonnull final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifier) { + final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = Builders.mapEntryBuilder() + .withNodeIdentifier(nodeIdentifier); + + + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(mapEntryBuilder); + try (final JsonParserStream jsonParser = JsonParserStream.create(writer, schemaContext, parentSchema)) { + final JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8)); + jsonParser.parse(reader); + } catch (IOException e) { + LOG.warn("Unable to close json parser. Ignoring exception", e); + } + + return mapEntryBuilder.build(); + } + private static void writeChildren(final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException { for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> child : data.getValue()) { nnWriter.write(child); |