From 9173c204d5f54e5758df0ee346fb7e3937e37825 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 25 Mar 2022 20:12:22 +0100 Subject: [PATCH] Reconstruct inference stack during normalization Centralize allocation of Inference, adjusting tests to take advantage of this facility. This also hides the public InstanceIdentifierContext, so that we get clarity as to the actual context in which we are executing. AbstractFieldsTranslatorTest is refactored to remove mocking and correctly bind to the undelying model. This temporarily picks up yang-model-util-7.0.15 for restconf-{common,nb-rfc8040}, so as to gain access to new methods of DataSchemaContext{Node,Tree}. JIRA: NETCONF-818 Change-Id: Iaf3362877261f3caaebfb4ec98ef6de792139f12 Signed-off-by: Robert Varga --- restconf/restconf-common/pom.xml | 6 + .../context/InstanceIdentifierContext.java | 217 ++++++++++++------ .../common/util/OperationsResourceUtils.java | 5 +- .../impl/JsonNormalizedNodeBodyReader.java | 27 +-- .../RestconfDocumentedExceptionMapper.java | 2 +- .../sal/restconf/impl/ControllerContext.java | 30 +-- .../sal/restconf/impl/DataNormalizer.java | 15 +- .../sal/restconf/impl/RestconfImpl.java | 16 +- .../restconf/impl/test/BrokerFacadeTest.java | 13 +- .../impl/test/InvokeRpcMethodTest.java | 2 +- .../restconf/impl/test/RestconfImplTest.java | 55 ++++- .../impl/test/URIParametersParsing.java | 2 +- restconf/restconf-nb-rfc8040/pom.xml | 6 + .../impl/RestconfDataServiceImpl.java | 14 +- .../rests/services/impl/RestconfImpl.java | 2 +- .../rfc8040/utils/parser/IdentifierCodec.java | 2 +- .../utils/parser/ParserIdentifier.java | 55 +---- .../YangInstanceIdentifierDeserializer.java | 58 ++++- .../databind/jaxrs/QueryParamsTest.java | 20 +- .../services/impl/CreateStreamUtilTest.java | 4 +- .../rests/services/impl/Netconf799Test.java | 24 +- ...stconfInvokeOperationsServiceImplTest.java | 10 +- .../parser/AbstractFieldsTranslatorTest.java | 116 +--------- .../parser/NetconfFieldsTranslatorTest.java | 13 +- .../parser/WriterFieldsTranslatorTest.java | 14 +- ...angInstanceIdentifierDeserializerTest.java | 42 ++-- 26 files changed, 408 insertions(+), 362 deletions(-) diff --git a/restconf/restconf-common/pom.xml b/restconf/restconf-common/pom.xml index 971be213b3..e7c9a0b323 100644 --- a/restconf/restconf-common/pom.xml +++ b/restconf/restconf-common/pom.xml @@ -35,6 +35,12 @@ org.opendaylight.yangtools yang-data-codec-xml + + org.opendaylight.yangtools + yang-data-util + + 7.0.15 + org.opendaylight.yangtools yang-data-impl diff --git a/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/context/InstanceIdentifierContext.java b/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/context/InstanceIdentifierContext.java index 09a5cd2a83..cfe136565a 100644 --- a/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/context/InstanceIdentifierContext.java +++ b/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/context/InstanceIdentifierContext.java @@ -18,139 +18,226 @@ import org.opendaylight.mdsal.dom.api.DOMMountPoint; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; -import org.opendaylight.yangtools.yang.model.api.ContainerLike; +import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; -public final class InstanceIdentifierContext { - private final YangInstanceIdentifier instanceIdentifier; - private final SchemaNode schemaNode; - private final DOMMountPoint mountPoint; - private final EffectiveModelContext schemaContext; +public abstract class InstanceIdentifierContext { + private static final class Root extends InstanceIdentifierContext { + private final @NonNull EffectiveModelContext context; - private InstanceIdentifierContext(final EffectiveModelContext context, final DOMMountPoint mountPoint) { - instanceIdentifier = YangInstanceIdentifier.empty(); - schemaContext = requireNonNull(context); - schemaNode = schemaContext; - this.mountPoint = mountPoint; + Root(final EffectiveModelContext context, final DOMMountPoint mountPoint) { + super(context, mountPoint); + this.context = requireNonNull(context); + } + + @Override + public EffectiveModelContext getSchemaContext() { + return context; + } + + @Override + public YangInstanceIdentifier getInstanceIdentifier() { + return YangInstanceIdentifier.empty(); + } + + @Override + public Inference inference() { + return SchemaInferenceStack.of(context).toInference(); + } + + @Override + InstanceIdentifierContext createWithConcapt(final List concatArgs) { + return new DataPath(context, getMountPoint(), SchemaInferenceStack.of(context), + YangInstanceIdentifier.create(concatArgs)); + } } - private InstanceIdentifierContext(final EffectiveModelContext context, final RpcDefinition rpc, - final DOMMountPoint mountPoint) { - instanceIdentifier = null; - schemaContext = requireNonNull(context); - schemaNode = requireNonNull(rpc); - this.mountPoint = mountPoint; + private static final class DataPath extends InstanceIdentifierContext { + private final @NonNull YangInstanceIdentifier path; + private final @NonNull SchemaInferenceStack stack; + + private DataPath(final SchemaNode schemaNode, final DOMMountPoint mountPoint, + final SchemaInferenceStack stack, final YangInstanceIdentifier path) { + super(schemaNode, mountPoint); + this.stack = requireNonNull(stack); + this.path = requireNonNull(path); + } + + static @NonNull DataPath of(final EffectiveModelContext context, final YangInstanceIdentifier path, + final DOMMountPoint mountPoint) { + final var nodeAndStack = DataSchemaContextTree.from(context).enterPath(path).orElseThrow(); + return new DataPath(nodeAndStack.node().getDataSchemaNode(), mountPoint, nodeAndStack.stack(), path); + } + + @Override + public YangInstanceIdentifier getInstanceIdentifier() { + return path; + } + + @Override + public Inference inference() { + return stack.toInference(); + } + + @Override + @NonNull + InstanceIdentifierContext createWithConcapt(final List concatArgs) { + final var newInstanceIdentifier = YangInstanceIdentifier.create( + Iterables.concat(path.getPathArguments(), concatArgs)); + return new DataPath(getSchemaNode(), getMountPoint(), stack, newInstanceIdentifier); + } } - private InstanceIdentifierContext(final EffectiveModelContext context, final ContainerLike rpcInputOutput, - final DOMMountPoint mountPoint) { - instanceIdentifier = null; - schemaContext = requireNonNull(context); - schemaNode = requireNonNull(rpcInputOutput); - this.mountPoint = mountPoint; + private static final class WithoutDataPath extends InstanceIdentifierContext { + private final @NonNull SchemaInferenceStack stack; + + private WithoutDataPath(final SchemaNode schemaNode, final DOMMountPoint mountPoint, + final SchemaInferenceStack stack) { + super(schemaNode, mountPoint); + this.stack = requireNonNull(stack); + } + + @Override + public Inference inference() { + return stack.toInference(); + } + + @Override + public @Nullable YangInstanceIdentifier getInstanceIdentifier() { + return null; + } + + @Override + InstanceIdentifierContext createWithConcapt(final List concatArgs) { + return this; + } } - @Deprecated(forRemoval = true) - public InstanceIdentifierContext(final YangInstanceIdentifier instanceIdentifier, final SchemaNode schemaNode, - final DOMMountPoint mountPoint, final EffectiveModelContext context) { - this.instanceIdentifier = instanceIdentifier; - this.schemaNode = schemaNode; + private final @NonNull SchemaNode schemaNode; + private final @Nullable DOMMountPoint mountPoint; + + InstanceIdentifierContext(final SchemaNode schemaNode, final DOMMountPoint mountPoint) { + this.schemaNode = requireNonNull(schemaNode); this.mountPoint = mountPoint; - schemaContext = context; } public static @NonNull InstanceIdentifierContext ofLocalRoot(final EffectiveModelContext context) { - return new InstanceIdentifierContext(context, null); + return new Root(context, null); } @VisibleForTesting public static @NonNull InstanceIdentifierContext ofLocalPath(final EffectiveModelContext context, final YangInstanceIdentifier path) { - return new InstanceIdentifierContext(requireNonNull(path), - DataSchemaContextTree.from(context).findChild(path).orElseThrow().getDataSchemaNode(), null, - requireNonNull(context)); + return DataPath.of(context, path, null); } // Legacy bierman02 invokeRpc() - public static @NonNull InstanceIdentifierContext ofLocalRpc(final EffectiveModelContext context, - // FIXME: this this method really needed? - final RpcDefinition rpc) { - return new InstanceIdentifierContext(context, rpc, null); + public static @NonNull InstanceIdentifierContext ofRpcInput(final EffectiveModelContext context, + // FIXME: this this method really needed? + final RpcDefinition rpc, final @Nullable DOMMountPoint mountPoint) { + final var stack = SchemaInferenceStack.of(context); + stack.enterSchemaTree(rpc.getQName()); + stack.enterSchemaTree(rpc.getInput().getQName()); + return new WithoutDataPath(rpc, mountPoint, stack); + } + + public static @NonNull InstanceIdentifierContext ofRpcOutput(final EffectiveModelContext context, + // FIXME: this this method really needed? + final RpcDefinition rpc, final @Nullable DOMMountPoint mountPoint) { + final var stack = SchemaInferenceStack.of(context); + stack.enterSchemaTree(rpc.getQName()); + stack.enterSchemaTree(rpc.getOutput().getQName()); + return new WithoutDataPath(rpc, mountPoint, stack); } // Invocations of various identifier-less details public static @NonNull InstanceIdentifierContext ofDataSchemaNode(final EffectiveModelContext context, final DataSchemaNode schemaNode) { - return new InstanceIdentifierContext(null, requireNonNull(schemaNode), null, requireNonNull(context)); + return new WithoutDataPath(schemaNode, null, + SchemaInferenceStack.ofInstantiatedPath(context, schemaNode.getPath())); } // Invocations of various identifier-less details, potentially having a mount point public static @NonNull InstanceIdentifierContext ofDataSchemaNode(final EffectiveModelContext context, final DataSchemaNode schemaNode, final @Nullable DOMMountPoint mountPoint) { - return new InstanceIdentifierContext(null, requireNonNull(schemaNode), mountPoint, requireNonNull(context)); + return new WithoutDataPath(schemaNode, mountPoint, + SchemaInferenceStack.ofSchemaPath(context, schemaNode.getPath())); } public static @NonNull InstanceIdentifierContext ofLocalRpcInput(final EffectiveModelContext context, // FIXME: this this method really needed? final RpcDefinition rpc) { - return new InstanceIdentifierContext(context, rpc.getInput(), null); + final var stack = SchemaInferenceStack.of(context); + stack.enterSchemaTree(rpc.getQName()); + stack.enterSchemaTree(rpc.getInput().getQName()); + return new WithoutDataPath(rpc.getInput(), null, stack); } public static @NonNull InstanceIdentifierContext ofLocalRpcOutput(final EffectiveModelContext context, // FIXME: we want to re-validate this, so might as well take a QName final RpcDefinition rpc) { - return new InstanceIdentifierContext(context, rpc, null); + final var stack = SchemaInferenceStack.of(context); + stack.enterSchemaTree(rpc.getQName()); + stack.enterSchemaTree(rpc.getOutput().getQName()); + return new WithoutDataPath(rpc, null, stack); + } + + public static @NonNull InstanceIdentifierContext ofAction(final SchemaInferenceStack stack, + final ActionDefinition schemaNode, final YangInstanceIdentifier path, + final @Nullable DOMMountPoint mountPoint) { + return new DataPath(schemaNode, mountPoint, stack, path); + } + + public static @NonNull InstanceIdentifierContext ofPath(final SchemaInferenceStack stack, + final SchemaNode schemaNode, final YangInstanceIdentifier path, + final @Nullable DOMMountPoint mountPoint) { + return new DataPath(schemaNode, mountPoint, stack, path); } public static @NonNull InstanceIdentifierContext ofMountPointRoot(final DOMMountPoint mountPoint, final EffectiveModelContext mountContext) { - return new InstanceIdentifierContext(mountContext, requireNonNull(mountPoint)); + return new Root(mountContext, requireNonNull(mountPoint)); } @VisibleForTesting public static @NonNull InstanceIdentifierContext ofMountPointPath(final DOMMountPoint mountPoint, final EffectiveModelContext context, final YangInstanceIdentifier path) { - return new InstanceIdentifierContext(requireNonNull(path), - DataSchemaContextTree.from(context).findChild(path).orElseThrow().getDataSchemaNode(), - requireNonNull(mountPoint), requireNonNull(context)); - } - - public static @NonNull InstanceIdentifierContext ofMountPointRpc(final DOMMountPoint mountPoint, - final EffectiveModelContext mountContext, final RpcDefinition rpc) { - return new InstanceIdentifierContext(mountContext, rpc, requireNonNull(mountPoint)); + return DataPath.of(context, path, requireNonNull(mountPoint)); } public static @NonNull InstanceIdentifierContext ofMountPointRpcOutput(final DOMMountPoint mountPoint, final EffectiveModelContext mountContext, final RpcDefinition rpc) { - return new InstanceIdentifierContext(mountContext, rpc, requireNonNull(mountPoint)); + final var stack = SchemaInferenceStack.of(mountContext); + stack.enterSchemaTree(rpc.getQName()); + stack.enterSchemaTree(rpc.getOutput().getQName()); + return new WithoutDataPath(rpc, requireNonNull(mountPoint), stack); } // FIXME: what the heck are the callers of this doing?! - public @NonNull InstanceIdentifierContext withConcatenatedArgs(final List concatArgs) { - if (instanceIdentifier == null || concatArgs.isEmpty()) { - return this; - } - final var newInstanceIdentifier = YangInstanceIdentifier.create( - Iterables.concat(instanceIdentifier.getPathArguments(), concatArgs)); - return new InstanceIdentifierContext(newInstanceIdentifier, schemaNode, mountPoint, schemaContext); + public final @NonNull InstanceIdentifierContext withConcatenatedArgs(final List concatArgs) { + return concatArgs.isEmpty() ? this : createWithConcapt(concatArgs); } - public YangInstanceIdentifier getInstanceIdentifier() { - return instanceIdentifier; - } + abstract @NonNull InstanceIdentifierContext createWithConcapt(List concatArgs); - public SchemaNode getSchemaNode() { + public final @NonNull SchemaNode getSchemaNode() { return schemaNode; } - public DOMMountPoint getMountPoint() { + public final @Nullable DOMMountPoint getMountPoint() { return mountPoint; } - public EffectiveModelContext getSchemaContext() { - return schemaContext; + public @NonNull EffectiveModelContext getSchemaContext() { + return inference().getEffectiveModelContext(); } + + public abstract @NonNull Inference inference(); + + public abstract @Nullable YangInstanceIdentifier getInstanceIdentifier(); } diff --git a/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/OperationsResourceUtils.java b/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/OperationsResourceUtils.java index 7460f92aaa..055e57ad0f 100644 --- a/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/OperationsResourceUtils.java +++ b/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/OperationsResourceUtils.java @@ -60,7 +60,8 @@ public final class OperationsResourceUtils { operationsBuilder.withChild(ImmutableNodes.leafNode(leaf.getQName(), Empty.value())); } - return Map.entry(new InstanceIdentifierContext(null, operatationsSchema, mountPoint, - new OperationsEffectiveModuleContext(ImmutableSet.copyOf(modules))), operationsBuilder.build()); + return Map.entry(InstanceIdentifierContext.ofDataSchemaNode( + new OperationsEffectiveModuleContext(ImmutableSet.copyOf(modules)), operatationsSchema, mountPoint), + operationsBuilder.build()); } } diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java index a386554012..aecea7a17f 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java @@ -46,9 +46,8 @@ 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.data.impl.schema.ResultAlreadySetException; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; -import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -109,21 +108,23 @@ public class JsonNormalizedNodeBodyReader final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); - final SchemaInferenceStack parentSchema; - if (isPost) { - // FIXME: We need dispatch for RPC. - parentSchema = SchemaInferenceStack.ofSchemaPath(path.getSchemaContext(), path.getSchemaNode().getPath()); - } else if (path.getSchemaNode() instanceof SchemaContext - || SchemaPath.ROOT.equals(path.getSchemaNode().getPath().getParent())) { - parentSchema = SchemaInferenceStack.of(path.getSchemaContext()); + final Inference parentInference; + if (isPost && !(path.getSchemaNode() instanceof RpcDefinition)) { + parentInference = path.inference(); } else { - parentSchema = SchemaInferenceStack.ofSchemaPath(path.getSchemaContext(), - path.getSchemaNode().getPath().getParent()); + final var inference = path.inference(); + if (!inference.statementPath().isEmpty()) { + final var stack = inference.toSchemaInferenceStack(); + stack.exit(); + parentInference = stack.toInference(); + } else { + parentInference = inference; + } } final JsonParserStream jsonParser = JsonParserStream.create(writer, JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(path.getSchemaContext()), - parentSchema.toInference()); + parentInference); final JsonReader reader = new JsonReader(new InputStreamReader(nonEmptyInputStreamOptional.get(), StandardCharsets.UTF_8)); jsonParser.parse(reader); diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDocumentedExceptionMapper.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDocumentedExceptionMapper.java index 1803a4b3ba..3975aa7de5 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDocumentedExceptionMapper.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDocumentedExceptionMapper.java @@ -144,7 +144,7 @@ public class RestconfDocumentedExceptionMapper implements ExceptionMapper mountOpt = mountService.getMountPoint(partialPath); if (mountOpt.isEmpty()) { LOG.debug("Instance identifier to missing mount point: {}", partialPath); @@ -631,8 +632,8 @@ public final class ControllerContext implements EffectiveModelContextListener, C rpc = getRpcDefinition(module, rpcName); } if (rpc != null) { - return mountPoint == null ? InstanceIdentifierContext.ofLocalRpc(globalSchema, rpc) - : InstanceIdentifierContext.ofMountPointRpc(mountPoint, getModelContext(mountPoint), rpc); + final var ctx = mountPoint == null ? globalSchema : getModelContext(mountPoint); + return InstanceIdentifierContext.ofRpcInput(ctx, rpc, mountPoint); } } @@ -717,8 +718,19 @@ public final class ControllerContext implements EffectiveModelContextListener, C private static InstanceIdentifierContext createContext(final YangInstanceIdentifier instance, final DataSchemaNode dataSchemaNode, final DOMMountPoint mountPoint, final EffectiveModelContext schemaContext) { - final YangInstanceIdentifier instanceIdentifier = new DataNormalizer(schemaContext).toNormalized(instance); - return new InstanceIdentifierContext(instanceIdentifier, dataSchemaNode, mountPoint, schemaContext); + final var normalized = new DataNormalizer(schemaContext).toNormalized(instance); + + // FIXME: Verification before we trust this + final var stack = normalized.getValue(); + if (!stack.isEmpty()) { + final var stackPath = stack.toSchemaPath(); + final var nodePath = dataSchemaNode.getPath(); + verify(stackPath.equals(nodePath), "Mismatched path: expected %s got %s", nodePath, stackPath); + } else { + verify(dataSchemaNode.equals(schemaContext), "Unexpected node %s", dataSchemaNode); + } + + return InstanceIdentifierContext.ofPath(normalized.getValue(), dataSchemaNode, normalized.getKey(), mountPoint); } public static DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, @@ -953,14 +965,6 @@ public final class ControllerContext implements EffectiveModelContextListener, C return builder.toString(); } - public YangInstanceIdentifier toNormalized(final YangInstanceIdentifier legacy) { - try { - return dataNormalizer.toNormalized(legacy); - } catch (final NullPointerException e) { - throw new RestconfDocumentedException("Data normalizer isn't set. Normalization isn't possible", e); - } - } - public YangInstanceIdentifier toXpathRepresentation(final YangInstanceIdentifier instanceIdentifier) { try { return dataNormalizer.toLegacy(instanceIdentifier); diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizer.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizer.java index 81224b5f36..334a6c7413 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizer.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizer.java @@ -8,38 +8,45 @@ package org.opendaylight.netconf.sal.restconf.impl; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; class DataNormalizer { private final DataNormalizationOperation operation; + private final EffectiveModelContext context; DataNormalizer(final EffectiveModelContext ctx) { + context = requireNonNull(ctx); operation = DataNormalizationOperation.from(ctx); } - YangInstanceIdentifier toNormalized(final YangInstanceIdentifier legacy) { + Entry toNormalized(final YangInstanceIdentifier legacy) { List normalizedArgs = new ArrayList<>(); DataNormalizationOperation currentOp = operation; Iterator arguments = legacy.getPathArguments().iterator(); + SchemaInferenceStack stack = SchemaInferenceStack.of(context); try { while (arguments.hasNext()) { PathArgument legacyArg = arguments.next(); - currentOp = currentOp.getChild(legacyArg); + currentOp = currentOp.enterChild(legacyArg, stack); checkArgument(currentOp != null, "Legacy Instance Identifier %s is not correct. Normalized Instance Identifier so far %s", legacy, normalizedArgs); while (currentOp.isMixin()) { normalizedArgs.add(currentOp.getIdentifier()); - currentOp = currentOp.getChild(legacyArg.getNodeType()); + currentOp = currentOp.enterChild(legacyArg.getNodeType(), stack); } normalizedArgs.add(legacyArg); } @@ -47,7 +54,7 @@ class DataNormalizer { throw new IllegalArgumentException("Failed to normalize path " + legacy, e); } - return YangInstanceIdentifier.create(normalizedArgs); + return Map.entry(YangInstanceIdentifier.create(normalizedArgs), stack); } DataNormalizationOperation getOperation(final YangInstanceIdentifier legacy) diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java index 409033933c..bdcf24dbb0 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java @@ -219,8 +219,9 @@ public final class RestconfImpl implements RestconfService { SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); moduleContainerBuilder.withChild(allModuleMap); - return new NormalizedNodeContext(InstanceIdentifierContext.ofDataSchemaNode(schemaContext, modulesSchemaNode), - moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); + return new NormalizedNodeContext( + InstanceIdentifierContext.ofDataSchemaNode(schemaContext, modulesSchemaNode, null), + moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); } /** @@ -251,8 +252,8 @@ public final class RestconfImpl implements RestconfService { moduleContainerBuilder.withChild(mountPointModulesMap); return new NormalizedNodeContext( - InstanceIdentifierContext.ofDataSchemaNode(controllerContext.getGlobalSchema(), modulesSchemaNode), - moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); + InstanceIdentifierContext.ofDataSchemaNode(controllerContext.getGlobalSchema(), modulesSchemaNode, null), + moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); } @Override @@ -448,10 +449,9 @@ public final class RestconfImpl implements RestconfService { } final var resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode(); - final var info = mountPoint == null ? InstanceIdentifierContext.ofLocalRpc(schemaContext, resultNodeSchema) - : InstanceIdentifierContext.ofMountPointRpcOutput(mountPoint, schemaContext, resultNodeSchema); - - return new NormalizedNodeContext(info, resultData, QueryParametersParser.parseWriterParameters(uriInfo)); + return new NormalizedNodeContext( + InstanceIdentifierContext.ofRpcOutput(schemaContext, resultNodeSchema, mountPoint), resultData, + QueryParametersParser.parseWriterParameters(uriInfo)); } @SuppressFBWarnings(value = "NP_LOAD_OF_KNOWN_NULL_VALUE", diff --git a/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java b/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java index 395f644510..04cbb967ff 100644 --- a/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java +++ b/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java @@ -82,6 +82,7 @@ import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; /** * Unit tests for BrokerFacade. @@ -364,8 +365,8 @@ public class BrokerFacadeTest { when(patchContext.getData()).thenReturn(List.of()); // no mount point - doReturn(InstanceIdentifierContext.ofDataSchemaNode( - mock(EffectiveModelContext.class), mock(DataSchemaNode.class))) + doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)), + mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), null)) .when(patchContext).getInstanceIdentifierContext(); doReturn(CommitInfo.emptyFluentFuture()).when(rwTransaction).commit(); @@ -388,8 +389,8 @@ public class BrokerFacadeTest { when(patchContext.getData()).thenReturn(List.of()); // return mount point with broker - doReturn(InstanceIdentifierContext.ofDataSchemaNode( - mock(EffectiveModelContext.class), mock(DataSchemaNode.class), mountPoint)) + doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)), + mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), mountPoint)) .when(patchContext).getInstanceIdentifierContext(); when(mountPoint.getService(DOMDataBroker.class)).thenReturn(Optional.of(mountDataBroker)); @@ -412,8 +413,8 @@ public class BrokerFacadeTest { final PatchContext patchContext = mock(PatchContext.class); final DOMMountPoint mountPoint = mock(DOMMountPoint.class); - doReturn(InstanceIdentifierContext.ofDataSchemaNode( - mock(EffectiveModelContext.class), mock(DataSchemaNode.class), mountPoint)) + doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)), + mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), mountPoint)) .when(patchContext).getInstanceIdentifierContext(); // missing broker on mounted device diff --git a/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java b/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java index 4aa1c8ade8..56472e534b 100644 --- a/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java +++ b/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java @@ -148,7 +148,7 @@ public class InvokeRpcMethodTest { contNode.withChild(lfNode); container.withChild(contNode.build()); - return new NormalizedNodeContext(InstanceIdentifierContext.ofLocalRpc(schema, rpcSchemaNode), + return new NormalizedNodeContext(InstanceIdentifierContext.ofRpcInput(schema, rpcSchemaNode, null), container.build()); } diff --git a/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java b/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java index 8460a1afc8..dfc48e73e6 100644 --- a/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java +++ b/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java @@ -17,6 +17,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture; import com.google.common.collect.Iterables; @@ -58,6 +59,7 @@ import org.opendaylight.yangtools.yang.common.Empty; import org.opendaylight.yangtools.yang.common.ErrorTag; import org.opendaylight.yangtools.yang.common.ErrorType; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.YangConstants; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; @@ -69,11 +71,17 @@ import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNode import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.InputSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; @@ -146,18 +154,39 @@ public class RestconfImplTest { @Test public void testRpcForMountpoint() throws Exception { + final QName qname = QName.create("namespace", "2010-10-10", "localname"); final UriInfo uriInfo = mock(UriInfo.class); doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters(anyBoolean()); final NormalizedNodeContext ctx = mock(NormalizedNodeContext.class); - final RpcDefinition schemaNode = mock(RpcDefinition.class); - doReturn(mock(SchemaPath.class)).when(schemaNode).getPath(); - doReturn(QName.create("namespace", "2010-10-10", "localname")).when(schemaNode).getQName(); + final RpcDefinition rpc = mock(RpcDefinition.class, + withSettings().extraInterfaces(RpcEffectiveStatement.class)); + doReturn(mock(SchemaPath.class)).when(rpc).getPath(); + doReturn(qname).when(rpc).getQName(); + + final InputSchemaNode input = mock(InputSchemaNode.class, + withSettings().extraInterfaces(InputEffectiveStatement.class)); + final QName inputQName = YangConstants.operationInputQName(qname.getModule()); + doReturn(input).when(rpc).getInput(); + doReturn(inputQName).when(input).getQName(); + doReturn(Optional.of(input)).when((RpcEffectiveStatement) rpc).findSchemaTreeNode(inputQName); + + final OutputSchemaNode output = mock(OutputSchemaNode.class, + withSettings().extraInterfaces(OutputEffectiveStatement.class)); + final QName outputQName = YangConstants.operationInputQName(qname.getModule()); + doReturn(output).when(rpc).getOutput(); + doReturn(outputQName).when(output).getQName(); + doReturn(Optional.of(output)).when((RpcEffectiveStatement) rpc).findSchemaTreeNode(outputQName); + + final EffectiveModelContext mountContext = mock(EffectiveModelContext.class); + final ModuleEffectiveStatement mountModule = mock(ModuleEffectiveStatement.class); + doReturn(Map.of(qname.getModule(), mountModule)).when(mountContext).getModuleStatements(); + doReturn(Optional.of(rpc)).when(mountModule).findSchemaTreeNode(qname); final DOMMountPoint mount = mock(DOMMountPoint.class); - doReturn(Optional.of(FixedDOMSchemaService.of(schemaContext))).when(mount).getService(DOMSchemaService.class); + doReturn(Optional.of(FixedDOMSchemaService.of(mountContext))).when(mount).getService(DOMSchemaService.class); - doReturn(InstanceIdentifierContext.ofMountPointRpc(mount, schemaContext, schemaNode)) + doReturn(InstanceIdentifierContext.ofRpcInput(mountContext, rpc, mount)) .when(ctx).getInstanceIdentifierContext(); final DOMRpcService rpcService = mock(DOMRpcService.class); @@ -174,15 +203,17 @@ public class RestconfImplTest { */ @Test public void createNotificationStreamTest() { - final NormalizedNodeContext payload = mock(NormalizedNodeContext.class); + final QName rpcQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", + "2014-01-14", "create-notification-stream"); - final RpcDefinition schemaNode = mock(RpcDefinition.class); - doReturn(mock(SchemaPath.class)).when(schemaNode).getPath(); - doReturn(InstanceIdentifierContext.ofLocalRpc(schemaContext, schemaNode)).when(payload) - .getInstanceIdentifierContext(); + final RpcDefinition schemaNode = schemaContext.getOperations().stream() + .filter(rpc -> rpc.getQName().equals(rpcQName)) + .findFirst() + .orElseThrow(); - doReturn(QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", - "2014-01-14", "create-notification-stream")).when(schemaNode).getQName(); + final NormalizedNodeContext payload = mock(NormalizedNodeContext.class); + doReturn(InstanceIdentifierContext.ofRpcInput(schemaContext, schemaNode, null)).when(payload) + .getInstanceIdentifierContext(); final Set children = new HashSet<>(); final LeafSetNode child = mock(LeafSetNode.class); diff --git a/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java b/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java index a022c0e789..1d36857b83 100644 --- a/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java +++ b/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java @@ -169,6 +169,6 @@ public class URIParametersParsing { when(rpcDef.getPath()).thenReturn(SchemaPath.create(true, rpcQName)); when(rpcDef.getQName()).thenReturn(rpcQName); - return new NormalizedNodeContext(InstanceIdentifierContext.ofLocalRpc(schema, rpcDef), container.build()); + return new NormalizedNodeContext(InstanceIdentifierContext.ofRpcInput(schema, rpcDef, null), container.build()); } } diff --git a/restconf/restconf-nb-rfc8040/pom.xml b/restconf/restconf-nb-rfc8040/pom.xml index 89154c9a1c..0818e8149d 100644 --- a/restconf/restconf-nb-rfc8040/pom.xml +++ b/restconf/restconf-nb-rfc8040/pom.xml @@ -77,6 +77,12 @@ org.opendaylight.yangtools yang-data-api + + org.opendaylight.yangtools + yang-data-util + + 7.0.15 + org.opendaylight.yangtools yang-data-impl diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java index 60a21d4ee0..69a1fdb8d5 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java @@ -330,24 +330,16 @@ public class RestconfDataServiceImpl implements RestconfDataService { final Absolute schemaPath = Absolute.of(ImmutableList.copyOf(context.getSchemaNode().getPath() .getPathFromRoot())); final DOMActionResult response; - final EffectiveModelContext schemaContextRef; if (mountPoint != null) { response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, mountPoint); - schemaContextRef = modelContext(mountPoint); } else { response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, actionService); - schemaContextRef = schemaContextHandler.get(); } final DOMActionResult result = checkActionResponse(response); - ActionDefinition resultNodeSchema = null; ContainerNode resultData = null; if (result != null) { - final Optional optOutput = result.getOutput(); - if (optOutput.isPresent()) { - resultData = optOutput.get(); - resultNodeSchema = (ActionDefinition) context.getSchemaNode(); - } + resultData = result.getOutput().orElse(null); } if (resultData != null && resultData.isEmpty()) { @@ -355,9 +347,7 @@ public class RestconfDataServiceImpl implements RestconfDataService { } return Response.status(Status.OK) - .entity(NormalizedNodePayload.ofNullable( - new InstanceIdentifierContext(yangIIdContext, resultNodeSchema, mountPoint, schemaContextRef), - resultData)) + .entity(NormalizedNodePayload.ofNullable(context, resultData)) .build(); } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfImpl.java index 3106571426..915654dbaf 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfImpl.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfImpl.java @@ -50,7 +50,7 @@ public class RestconfImpl implements RestconfService { (LeafSchemaNode) ((ContainerSchemaNode) grouping.getDataChildByName(Restconf.QNAME)) .getDataChildByName(YANG_LIBRARY_VERSION); - return NormalizedNodePayload.of(InstanceIdentifierContext.ofDataSchemaNode(context, schemaNode), + return NormalizedNodePayload.of(InstanceIdentifierContext.ofDataSchemaNode(context, schemaNode, null), ImmutableNodes.leafNode(YANG_LIBRARY_VERSION, IetfYangLibrary.REVISION.toString())); } } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/IdentifierCodec.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/IdentifierCodec.java index cda1b4459c..9e89ddc267 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/IdentifierCodec.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/IdentifierCodec.java @@ -27,6 +27,6 @@ public final class IdentifierCodec { public static YangInstanceIdentifier deserialize(final String data, final EffectiveModelContext schemaContext) { return data == null ? YangInstanceIdentifier.empty() - : YangInstanceIdentifier.create(YangInstanceIdentifierDeserializer.create(schemaContext, data)); + : YangInstanceIdentifierDeserializer.create(schemaContext, data).path; } } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java index d4aabad83e..8148406579 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java @@ -8,7 +8,6 @@ package org.opendaylight.restconf.nb.rfc8040.utils.parser; import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Verify.verifyNotNull; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; @@ -37,16 +36,9 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.common.YangNames; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; -import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; -import org.opendaylight.yangtools.yang.model.api.ActionDefinition; -import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -117,55 +109,14 @@ public final class ParserIdentifier { */ private static InstanceIdentifierContext createIIdContext(final EffectiveModelContext schemaContext, final String url, final @Nullable DOMMountPoint mountPoint) { - final YangInstanceIdentifier urlPath = IdentifierCodec.deserialize(url, schemaContext); // First things first: an empty path means data invocation on SchemaContext - if (urlPath.isEmpty()) { + if (url == null) { return mountPoint != null ? InstanceIdentifierContext.ofMountPointRoot(mountPoint, schemaContext) : InstanceIdentifierContext.ofLocalRoot(schemaContext); } - return new InstanceIdentifierContext(urlPath, getPathSchema(schemaContext, urlPath), mountPoint, schemaContext); - } - - private static SchemaNode getPathSchema(final EffectiveModelContext schemaContext, - final YangInstanceIdentifier urlPath) { - // Peel the last component and locate the parent data node, empty path resolves to SchemaContext - final DataSchemaContextNode parent = DataSchemaContextTree.from(schemaContext) - .findChild(verifyNotNull(urlPath.getParent())) - .orElseThrow( - // Parent data node is not present, this is not a valid location. - () -> new RestconfDocumentedException("Parent of " + urlPath + " not found", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE)); - - // Now try to resolve the last component as a data item... - final DataSchemaContextNode data = parent.getChild(urlPath.getLastPathArgument()); - if (data != null) { - return data.getDataSchemaNode(); - } - - // ... otherwise this has to be an operation invocation. RPCs cannot be defined anywhere but schema root, - // actions can reside everywhere else (and SchemaContext reports them empty) - final QName qname = urlPath.getLastPathArgument().getNodeType(); - final DataSchemaNode parentSchema = parent.getDataSchemaNode(); - if (parentSchema instanceof SchemaContext) { - for (final RpcDefinition rpc : ((SchemaContext) parentSchema).getOperations()) { - if (qname.equals(rpc.getQName())) { - return rpc; - } - } - } - if (parentSchema instanceof ActionNodeContainer) { - for (final ActionDefinition action : ((ActionNodeContainer) parentSchema).getActions()) { - if (qname.equals(action.getQName())) { - return action; - } - } - } - - // No luck: even if we found the parent, we did not locate a data, nor RPC, nor action node, hence the URL - // is deemed invalid - throw new RestconfDocumentedException("Context for " + urlPath + " not found", ErrorType.PROTOCOL, - ErrorTag.INVALID_VALUE); + final var result = YangInstanceIdentifierDeserializer.create(schemaContext, url); + return InstanceIdentifierContext.ofPath(result.stack, result.node, result.path, mountPoint); } /** diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java index d4c6b1cd4e..dd206f8443 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java @@ -7,10 +7,10 @@ */ package org.opendaylight.restconf.nb.rfc8040.utils.parser; +import static com.google.common.base.Verify.verify; import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.text.ParseException; import java.util.ArrayList; @@ -37,7 +37,9 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement; @@ -49,6 +51,34 @@ import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; * Deserializer for {@link String} to {@link YangInstanceIdentifier} for restconf. */ public final class YangInstanceIdentifierDeserializer { + public static final class Result { + public final @NonNull YangInstanceIdentifier path; + public final @NonNull SchemaInferenceStack stack; + public final @NonNull SchemaNode node; + + Result(final EffectiveModelContext context) { + path = YangInstanceIdentifier.empty(); + node = requireNonNull(context); + stack = SchemaInferenceStack.of(context); + } + + Result(final EffectiveModelContext context, final QName qname) { + // Legacy behavior: RPCs do not really have a YangInstanceIdentifier, but the rest of the code expects it + path = YangInstanceIdentifier.of(qname); + stack = SchemaInferenceStack.of(context); + + final var stmt = stack.enterSchemaTree(qname); + verify(stmt instanceof RpcDefinition, "Unexpected statement %s", stmt); + node = (RpcDefinition) stmt; + } + + Result(final List steps, final SchemaInferenceStack stack, final SchemaNode node) { + path = YangInstanceIdentifier.create(steps); + this.stack = requireNonNull(stack); + this.node = requireNonNull(node); + } + } + private final @NonNull EffectiveModelContext schemaContext; private final @NonNull ApiPath apiPath; @@ -65,7 +95,7 @@ public final class YangInstanceIdentifierDeserializer { * @return {@link Iterable} of {@link PathArgument} * @throws RestconfDocumentedException the path is not valid */ - public static List create(final EffectiveModelContext schemaContext, final String data) { + public static Result create(final EffectiveModelContext schemaContext, final String data) { final ApiPath path; try { path = ApiPath.parse(requireNonNull(data)); @@ -76,7 +106,7 @@ public final class YangInstanceIdentifierDeserializer { return create(schemaContext, path); } - public static List create(final EffectiveModelContext schemaContext, final ApiPath path) { + public static Result create(final EffectiveModelContext schemaContext, final ApiPath path) { return new YangInstanceIdentifierDeserializer(schemaContext, path).parse(); } @@ -88,10 +118,10 @@ public final class YangInstanceIdentifierDeserializer { // // All of this really is an utter mess because we end up calling into this code from various places which, // for example, should not allow RPCs to be valid targets - private ImmutableList parse() { + private Result parse() { final var it = apiPath.steps().iterator(); if (!it.hasNext()) { - return ImmutableList.of(); + return new Result(schemaContext); } // First step is somewhat special: @@ -120,16 +150,19 @@ public final class YangInstanceIdentifierDeserializer { + "therefore it must not contain key values", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } - // Legacy behavior: RPCs do not really have a YangInstanceIdentifier, but the rest of the code expects it - return ImmutableList.of(new NodeIdentifier(qname)); + return new Result(schemaContext, optRpc.orElseThrow().argument()); } + final var stack = SchemaInferenceStack.of(schemaContext); final var path = new ArrayList(); + final SchemaNode node; + var parentNode = DataSchemaContextTree.from(schemaContext).getRoot(); while (true) { final var parentSchema = parentNode.getDataSchemaNode(); if (parentSchema instanceof ActionNodeContainer) { - if (((ActionNodeContainer) parentSchema).findAction(qname).isPresent()) { + final var optAction = ((ActionNodeContainer) parentSchema).findAction(qname); + if (optAction.isPresent()) { if (it.hasNext()) { throw new RestconfDocumentedException("Request path resolves to action '" + qname + "' and " + "therefore it must not continue past it", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); @@ -142,19 +175,21 @@ public final class YangInstanceIdentifierDeserializer { // Legacy behavior: Action's path should not include its path, but the rest of the code expects it path.add(new NodeIdentifier(qname)); + stack.enterSchemaTree(qname); + node = optAction.orElseThrow(); break; } } // Resolve the child step with respect to data schema tree - final var found = RestconfDocumentedException.throwIfNull(parentNode.getChild(qname), + final var found = RestconfDocumentedException.throwIfNull(parentNode.enterChild(stack, qname), ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "Schema for '%s' not found", qname); // Now add all mixins encountered to the path var childNode = found; while (childNode.isMixin()) { path.add(childNode.getIdentifier()); - childNode = verifyNotNull(childNode.getChild(qname), + childNode = verifyNotNull(childNode.enterChild(stack, qname), "Mixin %s is missing child for %s while resolving %s", childNode, qname, found); } @@ -175,6 +210,7 @@ public final class YangInstanceIdentifierDeserializer { path.add(pathArg); if (!it.hasNext()) { + node = childNode.getDataSchemaNode(); break; } @@ -188,7 +224,7 @@ public final class YangInstanceIdentifierDeserializer { qname = step.identifier().bindTo(namespace); } - return ImmutableList.copyOf(path); + return new Result(path, stack, node); } private NodeIdentifierWithPredicates prepareNodeWithPredicates(final QName qname, diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParamsTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParamsTest.java index 231620c789..888a5a56d9 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParamsTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParamsTest.java @@ -15,8 +15,11 @@ import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import javax.ws.rs.core.MultivaluedHashMap; @@ -40,6 +43,9 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement; @RunWith(MockitoJUnitRunner.StrictStubs.class) public class QueryParamsTest { @@ -162,14 +168,22 @@ public class QueryParamsTest { assertNotNull(params.fields()); // fields for write filtering - final var containerSchema = mock(ContainerSchemaNode.class); - doReturn(QName.create(containerChild, "container")).when(containerSchema).getQName(); + final var containerSchema = mock(ContainerSchemaNode.class, + withSettings().extraInterfaces(ContainerEffectiveStatement.class)); + final var containerQName = QName.create(containerChild, "container"); + doReturn(containerQName).when(containerSchema).getQName(); + doReturn(SchemaPath.create(true, containerQName)).when(containerSchema).getPath(); final var containerChildSchema = mock(LeafSchemaNode.class); doReturn(containerChild).when(containerChildSchema).getQName(); doReturn(containerChildSchema).when(containerSchema).dataChildByName(containerChild); + final var module = mock(ModuleEffectiveStatement.class); + doReturn(Optional.of(containerSchema)).when(module).findSchemaTreeNode(containerQName); + final var context = mock(EffectiveModelContext.class); + doReturn(Map.of(containerQName.getModule(), module)).when(context).getModuleStatements(); + final QueryParameters queryParameters = QueryParams.newQueryParameters(params, - InstanceIdentifierContext.ofDataSchemaNode(mock(EffectiveModelContext.class), containerSchema)); + InstanceIdentifierContext.ofDataSchemaNode(context, containerSchema)); final List> fields = queryParameters.fields(); assertNotNull(fields); assertEquals(1, fields.size()); diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/CreateStreamUtilTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/CreateStreamUtilTest.java index f1c79df595..d428819833 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/CreateStreamUtilTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/CreateStreamUtilTest.java @@ -123,7 +123,7 @@ public class CreateStreamUtilTest { .withValue(o).build(); container.withChild(lfNode); - return NormalizedNodePayload.of(new InstanceIdentifierContext(null, rpcInputSchemaNode, null, SCHEMA_CTX), - container.build()); + return NormalizedNodePayload.of( + InstanceIdentifierContext.ofDataSchemaNode(SCHEMA_CTX, rpcInputSchemaNode, null), container.build()); } } diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf799Test.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf799Test.java index ce2ab22c71..b959cf70a7 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf799Test.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf799Test.java @@ -7,6 +7,8 @@ */ package org.opendaylight.restconf.nb.rfc8040.rests.services.impl; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; @@ -16,7 +18,6 @@ import static org.mockito.Mockito.mock; import com.google.common.util.concurrent.Futures; import java.io.FileNotFoundException; -import java.util.Optional; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @@ -37,9 +38,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; -import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; -import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; @@ -75,9 +74,15 @@ public class Netconf799Test { mock(DOMMountPointService.class), mock(RestconfStreamsSubscriptionService.class), actionService, mock(Configuration.class)); - final var schemaNode = loadAction(contextRef, RESET_QNAME, ACTION_YII).orElseThrow(); + final var nodeAndStack = DataSchemaContextTree.from(contextRef).enterPath(ACTION_YII).orElseThrow(); + final var node = nodeAndStack.node().getDataSchemaNode(); + assertThat(node, instanceOf(ActionNodeContainer.class)); + final var actionNode = ((ActionNodeContainer) node).findAction(RESET_QNAME).orElseThrow(); + final var stack = nodeAndStack.stack(); + stack.enterSchemaTree(RESET_QNAME); + final var response = dataService.invokeAction(NormalizedNodePayload.of( - new InstanceIdentifierContext(ACTION_YII, schemaNode, null, contextRef), + InstanceIdentifierContext.ofAction(stack, actionNode, ACTION_YII, null), Builders.containerBuilder() .withNodeIdentifier(NodeIdentifier.create(INPUT_QNAME)) .withChild(ImmutableNodes.leafNode(DELAY_QNAME, Uint32.TEN)) @@ -86,13 +91,4 @@ public class Netconf799Test { assertEquals(204, response.getStatus()); assertNull(response.getEntity()); } - - private static Optional loadAction(final EffectiveModelContext modelContext, - final QName actionQName, final YangInstanceIdentifier actionYII) { - return DataSchemaContextTree.from(modelContext) - .findChild(actionYII) - .map(DataSchemaContextNode::getDataSchemaNode) - .flatMap(node -> node instanceof ActionNodeContainer ? ((ActionNodeContainer) node).findAction(actionQName) - : Optional.empty()); - } } diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImplTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImplTest.java index 33d1a85d7e..e18c8f4e82 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImplTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImplTest.java @@ -179,14 +179,16 @@ public class RestconfInvokeOperationsServiceImplTest { } private NormalizedNodePayload prepNNC(final NormalizedNode result) { - final RpcDefinition schemaNode = mock(RpcDefinition.class); - final QName qname = QName.create("invoke:rpc:module", "2013-12-03", "rpcTest"); - doReturn(qname).when(schemaNode).getQName(); + final QName qname = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test"); + final RpcDefinition schemaNode = CONTEXT.getOperations().stream().filter(rpc -> rpc.getQName().equals(qname)) + .findFirst() + .orElseThrow(); + final NormalizedNode data = mock(NormalizedNode.class); final DOMRpcResult domRpcResult = mock(DOMRpcResult.class); doReturn(immediateFluentFuture(domRpcResult)).when(rpcService).invokeRpc(qname, data); doReturn(result).when(domRpcResult).getResult(); return NormalizedNodePayload.of( - InstanceIdentifierContext.ofLocalRpc(CONTEXT, schemaNode), data); + InstanceIdentifierContext.ofRpcInput(CONTEXT, schemaNode, null), data); } } diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/AbstractFieldsTranslatorTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/AbstractFieldsTranslatorTest.java index 50a62a4059..96a31e8275 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/AbstractFieldsTranslatorTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/AbstractFieldsTranslatorTest.java @@ -10,16 +10,12 @@ package org.opendaylight.restconf.nb.rfc8040.utils.parser; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.when; import java.text.ParseException; import java.util.List; -import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.nb.rfc8040.FieldsParam; @@ -30,12 +26,7 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.common.XMLNamespace; -import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; -import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; public abstract class AbstractFieldsTranslatorTest { @@ -52,157 +43,68 @@ public abstract class AbstractFieldsTranslatorTest { // FIXME: remove all this mocking -- just parse the underlying model and be done with it // container jukebox - @Mock - private ContainerSchemaNode containerJukebox; private static final QName JUKEBOX_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "jukebox"); // container player - @Mock - private ContainerSchemaNode containerPlayer; protected static final QName PLAYER_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "player"); // container library - @Mock - private ContainerSchemaNode containerLibrary; protected static final QName LIBRARY_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "library"); + // list artist + protected static final QName ARTIST_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "artist"); + // container augmented library - @Mock - private ContainerSchemaNode augmentedContainerLibrary; protected static final QName AUGMENTED_LIBRARY_Q_NAME = QName.create(Q_NAME_MODULE_AUGMENTED_JUKEBOX, "augmented-library"); - // augmentation that contains speed leaf - @Mock - private AugmentationSchemaNode speedAugmentation; - // leaf speed - @Mock - private LeafSchemaNode leafSpeed; protected static final QName SPEED_Q_NAME = QName.create(Q_NAME_MODULE_AUGMENTED_JUKEBOX, "speed"); // list album - @Mock - private ListSchemaNode listAlbum; public static final QName ALBUM_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "album"); // leaf name - @Mock - private LeafSchemaNode leafName; protected static final QName NAME_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "name"); // container test data - @Mock - private ContainerSchemaNode containerTestData; private static final QName TEST_DATA_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "test-data"); // list services - @Mock - private ListSchemaNode listServices; protected static final QName SERVICES_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "services"); // leaf type-of-service - @Mock - private LeafSchemaNode leafTypeOfService; protected static final QName TYPE_OF_SERVICE_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "type-of-service"); // list instance - @Mock - private ListSchemaNode listInstance; protected static final QName INSTANCE_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "instance"); // leaf instance-name - @Mock - private LeafSchemaNode leafInstanceName; protected static final QName INSTANCE_NAME_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "instance-name"); // leaf provider - @Mock - private LeafSchemaNode leafProvider; protected static final QName PROVIDER_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "provider"); // container next-data - @Mock - private ContainerSchemaNode containerNextData; protected static final QName NEXT_DATA_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "next-data"); // leaf next-service - @Mock - private LeafSchemaNode leafNextService; protected static final QName NEXT_SERVICE_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "next-service"); // leaf-list protocols - @Mock - private LeafListSchemaNode leafListProtocols; protected static final QName PROTOCOLS_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "protocols"); @Before public void setUp() throws Exception { final EffectiveModelContext schemaContextJukebox = YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/jukebox")); - initJukeboxSchemaNodes(schemaContextJukebox); + identifierJukebox = InstanceIdentifierContext.ofDataSchemaNode(schemaContextJukebox, + schemaContextJukebox.getDataChildByName(JUKEBOX_Q_NAME)); final EffectiveModelContext schemaContextTestServices = YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/test-services")); - initTestServicesSchemaNodes(schemaContextTestServices); - } - - private void initJukeboxSchemaNodes(final EffectiveModelContext schemaContext) { - identifierJukebox = InstanceIdentifierContext.ofDataSchemaNode(schemaContext, containerJukebox); - when(containerJukebox.getQName()).thenReturn(JUKEBOX_Q_NAME); - - when(containerLibrary.getQName()).thenReturn(LIBRARY_Q_NAME); - when(containerJukebox.dataChildByName(LIBRARY_Q_NAME)).thenReturn(containerLibrary); - - when(augmentedContainerLibrary.getQName()).thenReturn(AUGMENTED_LIBRARY_Q_NAME); - when(containerJukebox.dataChildByName(AUGMENTED_LIBRARY_Q_NAME)) - .thenReturn(augmentedContainerLibrary); - - when(containerPlayer.getQName()).thenReturn(PLAYER_Q_NAME); - when(containerJukebox.dataChildByName(PLAYER_Q_NAME)).thenReturn(containerPlayer); - - when(listAlbum.getQName()).thenReturn(ALBUM_Q_NAME); - when(containerLibrary.dataChildByName(ALBUM_Q_NAME)).thenReturn(listAlbum); - - when(leafName.getQName()).thenReturn(NAME_Q_NAME); - when(listAlbum.dataChildByName(NAME_Q_NAME)).thenReturn(leafName); - - when(leafSpeed.getQName()).thenReturn(SPEED_Q_NAME); - when(leafSpeed.isAugmenting()).thenReturn(true); - when(containerPlayer.dataChildByName(SPEED_Q_NAME)).thenReturn(leafSpeed); - when(containerPlayer.getDataChildByName(SPEED_Q_NAME)).thenReturn(leafSpeed); - doReturn(List.of(leafSpeed)).when(speedAugmentation).getChildNodes(); - doReturn(List.of(speedAugmentation)).when(containerPlayer).getAvailableAugmentations(); - when(speedAugmentation.findDataChildByName(SPEED_Q_NAME)).thenReturn(Optional.of(leafSpeed)); - } - - private void initTestServicesSchemaNodes(final EffectiveModelContext schemaContext) { - identifierTestServices = InstanceIdentifierContext.ofDataSchemaNode(schemaContext, containerTestData); - when(containerTestData.getQName()).thenReturn(TEST_DATA_Q_NAME); - - when(listServices.getQName()).thenReturn(SERVICES_Q_NAME); - when(containerTestData.dataChildByName(SERVICES_Q_NAME)).thenReturn(listServices); - - when(leafListProtocols.getQName()).thenReturn(PROTOCOLS_Q_NAME); - when(containerTestData.dataChildByName(PROTOCOLS_Q_NAME)).thenReturn(leafListProtocols); - - when(leafTypeOfService.getQName()).thenReturn(TYPE_OF_SERVICE_Q_NAME); - when(listServices.dataChildByName(TYPE_OF_SERVICE_Q_NAME)).thenReturn(leafTypeOfService); - - when(listInstance.getQName()).thenReturn(INSTANCE_Q_NAME); - when(listServices.dataChildByName(INSTANCE_Q_NAME)).thenReturn(listInstance); - - when(leafInstanceName.getQName()).thenReturn(INSTANCE_NAME_Q_NAME); - when(listInstance.dataChildByName(INSTANCE_NAME_Q_NAME)).thenReturn(leafInstanceName); - - when(leafProvider.getQName()).thenReturn(PROVIDER_Q_NAME); - when(listInstance.dataChildByName(PROVIDER_Q_NAME)).thenReturn(leafProvider); - - when(containerNextData.getQName()).thenReturn(NEXT_DATA_Q_NAME); - when(listServices.dataChildByName(NEXT_DATA_Q_NAME)).thenReturn(containerNextData); - - when(leafNextService.getQName()).thenReturn(NEXT_SERVICE_Q_NAME); - when(containerNextData.dataChildByName(NEXT_SERVICE_Q_NAME)).thenReturn(leafNextService); + identifierTestServices = InstanceIdentifierContext.ofDataSchemaNode(schemaContextTestServices, + schemaContextTestServices.getDataChildByName(TEST_DATA_Q_NAME)); } protected abstract List translateFields(InstanceIdentifierContext context, FieldsParam fields); @@ -236,7 +138,7 @@ public abstract class AbstractFieldsTranslatorTest { */ @Test public void testSubPath() { - final var result = translateFields(identifierJukebox, assertFields("library/album/name")); + final var result = translateFields(identifierJukebox, assertFields("library/artist/album/name")); assertNotNull(result); assertSubPath(result); } @@ -248,7 +150,7 @@ public abstract class AbstractFieldsTranslatorTest { */ @Test public void testChildrenPath() { - final var result = translateFields(identifierJukebox, assertFields("library(album(name))")); + final var result = translateFields(identifierJukebox, assertFields("library(artist(album(name)))")); assertNotNull(result); assertChildrenPath(result); } diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/NetconfFieldsTranslatorTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/NetconfFieldsTranslatorTest.java index cbea2c49ea..137ae44c8e 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/NetconfFieldsTranslatorTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/NetconfFieldsTranslatorTest.java @@ -21,6 +21,7 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; /** * Unit test for {@link NetconfFieldsTranslator}. @@ -61,10 +62,11 @@ public class NetconfFieldsTranslatorTest extends AbstractFieldsTranslatorTest result) { assertEquals(1, result.size()); final var pathArguments = result.get(0).getPathArguments(); - assertEquals(3, pathArguments.size()); + assertEquals(4, pathArguments.size()); assertEquals(LIBRARY_Q_NAME, pathArguments.get(0).getNodeType()); - assertEquals(ALBUM_Q_NAME, pathArguments.get(1).getNodeType()); - assertEquals(NAME_Q_NAME, pathArguments.get(2).getNodeType()); + assertEquals(ARTIST_Q_NAME, pathArguments.get(1).getNodeType()); + assertEquals(ALBUM_Q_NAME, pathArguments.get(2).getNodeType()); + assertEquals(NAME_Q_NAME, pathArguments.get(3).getNodeType()); } @Override @@ -110,7 +112,10 @@ public class NetconfFieldsTranslatorTest extends AbstractFieldsTranslatorTest result) { assertEquals(1, result.size()); - assertEquals(List.of(new NodeIdentifier(SERVICES_Q_NAME), new NodeIdentifier(INSTANCE_Q_NAME)), + assertEquals(List.of( + // FIXME: this does not look right: where are the corresponding NodeIdentifiers? + NodeIdentifierWithPredicates.of(SERVICES_Q_NAME), + NodeIdentifierWithPredicates.of(INSTANCE_Q_NAME)), result.get(0).getPathArguments()); } diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/WriterFieldsTranslatorTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/WriterFieldsTranslatorTest.java index d809bedab7..d1f5a7ef2e 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/WriterFieldsTranslatorTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/WriterFieldsTranslatorTest.java @@ -42,18 +42,20 @@ public class WriterFieldsTranslatorTest extends AbstractFieldsTranslatorTest> result) { - assertEquals(3, result.size()); + assertEquals(4, result.size()); assertEquals(Set.of(LIBRARY_Q_NAME), result.get(0)); - assertEquals(Set.of(ALBUM_Q_NAME), result.get(1)); - assertEquals(Set.of(NAME_Q_NAME), result.get(2)); + assertEquals(Set.of(ARTIST_Q_NAME), result.get(1)); + assertEquals(Set.of(ALBUM_Q_NAME), result.get(2)); + assertEquals(Set.of(NAME_Q_NAME), result.get(3)); } @Override protected void assertChildrenPath(final List> result) { - assertEquals(3, result.size()); + assertEquals(4, result.size()); assertEquals(Set.of(LIBRARY_Q_NAME), result.get(0)); - assertEquals(Set.of(ALBUM_Q_NAME), result.get(1)); - assertEquals(Set.of(NAME_Q_NAME), result.get(2)); + assertEquals(Set.of(ARTIST_Q_NAME), result.get(1)); + assertEquals(Set.of(ALBUM_Q_NAME), result.get(2)); + assertEquals(Set.of(NAME_Q_NAME), result.get(3)); } @Override diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializerTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializerTest.java index 9a44c4c914..5bf0fb1c49 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializerTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializerTest.java @@ -69,7 +69,8 @@ public class YangInstanceIdentifierDeserializerTest { */ @Test public void deserializeContainerTest() { - final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA"); + final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA") + .path.getPathArguments(); assertEquals(1, result.size()); assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0)); } @@ -80,7 +81,8 @@ public class YangInstanceIdentifierDeserializerTest { */ @Test public void deserializeContainerWithLeafTest() { - final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A"); + final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A") + .path.getPathArguments(); assertEquals(2, result.size()); assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0)); assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")), result.get(1)); @@ -93,7 +95,7 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializeContainerWithListWithLeafListTest() { final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "deserializer-test:contA/list-A=100/leaf-list-AA=instance"); + "deserializer-test:contA/list-A=100/leaf-list-AA=instance").path.getPathArguments(); assertEquals(5, result.size()); // container @@ -116,7 +118,7 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializeContainerWithListWithActionTest() { final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "example-actions:interfaces/interface=eth0/reset"); + "example-actions:interfaces/interface=eth0/reset").path.getPathArguments(); assertEquals(4, result.size()); // container assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0)); @@ -135,7 +137,7 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializeContainerWithChoiceSchemaNodeWithActionTest() { final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "example-actions:interfaces/typeA-gigabyte/interface=eth0/reboot"); + "example-actions:interfaces/typeA-gigabyte/interface=eth0/reboot").path.getPathArguments(); assertEquals(6, result.size()); // container @@ -161,7 +163,7 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializeContainerWithChoiceCaseSchemaNodeWithActionTest() { final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "example-actions:interfaces/udp/reboot"); + "example-actions:interfaces/udp/reboot").path.getPathArguments(); assertEquals(4, result.size()); // container assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0)); @@ -179,7 +181,8 @@ public class YangInstanceIdentifierDeserializerTest { */ @Test public void deserializeListWithNoKeysTest() { - final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key"); + final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key") + .path.getPathArguments(); assertEquals(2, result.size()); final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key"); assertEquals(NodeIdentifier.create(list), result.get(0)); @@ -193,7 +196,7 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializeListWithOneKeyTest() { final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "deserializer-test:list-one-key=value"); + "deserializer-test:list-one-key=value").path.getPathArguments(); assertEquals(2, result.size()); final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key"); assertEquals(NodeIdentifier.create(list), result.get(0)); @@ -213,7 +216,7 @@ public class YangInstanceIdentifierDeserializerTest { QName.create(list, "enabled"), false); final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "deserializer-test:list-multiple-keys=value,100,false"); + "deserializer-test:list-multiple-keys=value,100,false").path.getPathArguments(); assertEquals(2, result.size()); assertEquals(NodeIdentifier.create(list), result.get(0)); assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1)); @@ -226,7 +229,7 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializeLeafListTest() { final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "deserializer-test:leaf-list-0=true"); + "deserializer-test:leaf-list-0=true").path.getPathArguments(); assertEquals(2, result.size()); final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0"); @@ -239,7 +242,7 @@ public class YangInstanceIdentifierDeserializerTest { */ @Test public void deserializeEmptyDataTest() { - assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "")); + assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "").path.getPathArguments()); } /** @@ -249,7 +252,7 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializeMultipleSlashesTest() throws ParseException { final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - ApiPath.parseUrl("deserializer-test:contA////list-A=40//list-key")); + ApiPath.parseUrl("deserializer-test:contA////list-A=40//list-key")).path.getPathArguments(); assertEquals(4, result.size()); // container @@ -561,7 +564,7 @@ public class YangInstanceIdentifierDeserializerTest { QName.create(list, "enabled"), false); final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "deserializer-test:list-multiple-keys=%3Afoo,1,false/string-value"); + "deserializer-test:list-multiple-keys=%3Afoo,1,false/string-value").path.getPathArguments(); assertEquals(3, result.size()); // list assertEquals(NodeIdentifier.create(list), result.get(0)); @@ -592,8 +595,7 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void percentEncodedKeyEndsWithNoPercentEncodedChars() { final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true"; - final YangInstanceIdentifier result = YangInstanceIdentifier.create( - YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI)); + final YangInstanceIdentifier result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI).path; final Iterator> resultListKeys = ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator(); @@ -615,7 +617,7 @@ public class YangInstanceIdentifierDeserializerTest { QName.create(list, "enabled"), true); final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "deserializer-test:list-multiple-keys=,0,true"); + "deserializer-test:list-multiple-keys=,0,true").path.getPathArguments(); assertEquals(2, result.size()); assertEquals(NodeIdentifier.create(list), result.get(0)); assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1)); @@ -640,7 +642,8 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializePartInOtherModuleTest() { final List result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "deserializer-test-included:augmented-list=100/deserializer-test:augmented-leaf"); + "deserializer-test-included:augmented-list=100/deserializer-test:augmented-leaf") + .path.getPathArguments(); assertEquals(4, result.size()); // list @@ -659,7 +662,8 @@ public class YangInstanceIdentifierDeserializerTest { @Test public void deserializeListInOtherModuleTest() { final List result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, - "deserializer-test-included:augmented-list=100/deserializer-test:augmenting-list=0"); + "deserializer-test-included:augmented-list=100/deserializer-test:augmenting-list=0") + .path.getPathArguments(); assertEquals(5, result.size()); // list @@ -695,7 +699,7 @@ public class YangInstanceIdentifierDeserializerTest { } private static void assertIdentityrefKeyValue(final String path) { - final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, path); + final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, path).path.getPathArguments(); assertEquals(4, pathArgs.size()); assertEquals("refs", pathArgs.get(0).getNodeType().getLocalName()); -- 2.36.6