Remove AbstractNormalizedNodeBodyReader 23/107523/18
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 28 Aug 2023 09:23:48 +0000 (11:23 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 10 Sep 2023 12:11:18 +0000 (14:11 +0200)
We are using this reader only in postData(). Refactor users to
centralized dispatch and move the logic to ChildBody and its
specializations.

This refactor also improves dispatch to datastore resource, as we know
that such POST requests are not action invocations.

Invocation path is using OperationInputBody, so as to share decoding
with the RPC invocation path.

JIRA: NETCONF-1128
Change-Id: Ie632de94dce24d3a194c560ae41e3e86d1e8ac72
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
31 files changed:
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/context/InstanceIdentifierContext.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/AbstractRestconfApplication.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/DataStreamApplication.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/JaxRsNorthbound.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestconfApplication.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/ChildBody.java [new file with mode: 0644]
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonChildBody.java [new file with mode: 0644]
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/OperationInputBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlChildBody.java [new file with mode: 0644]
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlOperationInputBody.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/AbstractNormalizedNodeBodyReader.java [deleted file]
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/JsonNormalizedNodeBodyReader.java [deleted file]
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlNormalizedNodeBodyReader.java [deleted file]
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/AbstractInstanceIdentifierTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractBodyTest.java [new file with mode: 0644]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractOperationInputBodyTest.java [new file with mode: 0644]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractResourceBodyTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonChildBodyTest.java [new file with mode: 0644]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonOperationInputBodyTest.java [new file with mode: 0644]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlChildBodyTest.java [new file with mode: 0644]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlOperationInputBodyTest.java [new file with mode: 0644]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/AbstractBodyReaderTest.java [deleted file]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/JsonBodyReaderTest.java [deleted file]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java [deleted file]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderTest.java [deleted file]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf799Test.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java
restconf/restconf-nb/src/test/resources/instanceidentifier/json/json_cont_action.json [deleted file]
restconf/restconf-nb/src/test/resources/instanceidentifier/xml/xml_cont_action.xml [deleted file]

index 2d0fbbe896be69c052def5bbb6d4e16c18f444f5..753abe5f9c83bb48746028e4dacee9ef2683a246 100644 (file)
@@ -11,13 +11,10 @@ import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Iterables;
-import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 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.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
@@ -48,12 +45,6 @@ public abstract class InstanceIdentifierContext {
         public Inference inference() {
             return SchemaInferenceStack.of(context).toInference();
         }
-
-        @Override
-        InstanceIdentifierContext createWithConcapt(final List<PathArgument> concatArgs) {
-            return new DataPath(context, getMountPoint(), SchemaInferenceStack.of(context),
-                YangInstanceIdentifier.of(concatArgs));
-        }
     }
 
     private static final class DataPath extends InstanceIdentifierContext {
@@ -82,14 +73,6 @@ public abstract class InstanceIdentifierContext {
         public Inference inference() {
             return stack.toInference();
         }
-
-        @Override
-        @NonNull
-        InstanceIdentifierContext createWithConcapt(final List<PathArgument> concatArgs) {
-            final var newInstanceIdentifier = YangInstanceIdentifier.of(
-                Iterables.concat(path.getPathArguments(), concatArgs));
-            return new DataPath(getSchemaNode(), getMountPoint(), stack, newInstanceIdentifier);
-        }
     }
 
     private static final class WithoutDataPath extends InstanceIdentifierContext {
@@ -110,11 +93,6 @@ public abstract class InstanceIdentifierContext {
         public @Nullable YangInstanceIdentifier getInstanceIdentifier() {
             return null;
         }
-
-        @Override
-        InstanceIdentifierContext createWithConcapt(final List<PathArgument> concatArgs) {
-            return this;
-        }
     }
 
     private final @NonNull SchemaNode schemaNode;
@@ -217,13 +195,6 @@ public abstract class InstanceIdentifierContext {
         return new WithoutDataPath(rpc, requireNonNull(mountPoint), stack);
     }
 
-    // FIXME: what the heck are the callers of this doing?!
-    public final @NonNull InstanceIdentifierContext withConcatenatedArgs(final List<PathArgument> concatArgs) {
-        return concatArgs.isEmpty() ? this : createWithConcapt(concatArgs);
-    }
-
-    abstract @NonNull InstanceIdentifierContext createWithConcapt(List<PathArgument> concatArgs);
-
     public final @NonNull SchemaNode getSchemaNode() {
         return schemaNode;
     }
index a309d2be00c0c80e36a91d7e50c59c66adb0f090..c4dd810baf927a819a6c6f6526e511282c5787b9 100644 (file)
@@ -13,11 +13,8 @@ import com.google.common.collect.ImmutableSet;
 import java.util.List;
 import java.util.Set;
 import javax.ws.rs.core.Application;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
-import org.opendaylight.restconf.nb.rfc8040.jersey.providers.JsonNormalizedNodeBodyReader;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.JsonNormalizedNodeBodyWriter;
-import org.opendaylight.restconf.nb.rfc8040.jersey.providers.XmlNormalizedNodeBodyReader;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.XmlNormalizedNodeBodyWriter;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.YangSchemaExportBodyWriter;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.YinSchemaExportBodyWriter;
@@ -30,13 +27,10 @@ import org.opendaylight.restconf.nb.rfc8040.jersey.providers.patch.XmlPatchStatu
  */
 abstract class AbstractRestconfApplication extends Application {
     private final DatabindProvider databindProvider;
-    private final DOMMountPointService mountPointService;
     private final List<Object> services;
 
-    AbstractRestconfApplication(final DatabindProvider databindProvider, final DOMMountPointService mountPointService,
-            final List<Object> services) {
+    AbstractRestconfApplication(final DatabindProvider databindProvider, final List<Object> services) {
         this.databindProvider = requireNonNull(databindProvider);
-        this.mountPointService = requireNonNull(mountPointService);
         this.services = requireNonNull(services);
     }
 
@@ -50,10 +44,8 @@ abstract class AbstractRestconfApplication extends Application {
 
     @Override
     public final Set<Object> getSingletons() {
-        return ImmutableSet.<Object>builderWithExpectedSize(services.size() + 5)
+        return ImmutableSet.<Object>builderWithExpectedSize(services.size() + 1)
             .addAll(services)
-            .add(new JsonNormalizedNodeBodyReader(databindProvider, mountPointService))
-            .add(new XmlNormalizedNodeBodyReader(databindProvider, mountPointService))
             .add(new RestconfDocumentedExceptionMapper(databindProvider))
             .build();
     }
index abf69e33912be49453857fc7bcbf52f979240c0a..e18302d2bd54fbb36f631ebd3352dd6ee6ac62f4 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.restconf.nb.rfc8040;
 import java.util.List;
 import javax.inject.Inject;
 import javax.inject.Singleton;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfDataStreamServiceImpl;
 
@@ -20,8 +19,8 @@ import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfDataStre
 @Singleton
 public class DataStreamApplication extends AbstractRestconfApplication {
     @Inject
-    public DataStreamApplication(final DatabindProvider databindProvider, final DOMMountPointService mountPointService,
+    public DataStreamApplication(final DatabindProvider databindProvider,
             final RestconfDataStreamServiceImpl dataStreamService) {
-        super(databindProvider, mountPointService, List.of(dataStreamService));
+        super(databindProvider, List.of(dataStreamService));
     }
 }
index 6f5e2fead8fc53d21685eede4295661e86253fdf..05967718f725597efe5c8ef66afaa20ac84489b6 100644 (file)
@@ -106,14 +106,16 @@ public final class JaxRsNorthbound implements AutoCloseable {
                 .addUrlPattern("/*")
                 .servlet(servletSupport.createHttpServletBuilder(
                     new RestconfApplication(databindProvider, mountPointService, dataBroker, rpcService, actionService,
-                        notificationService, schemaService, streamsConfiguration)).build())
+                        notificationService, schemaService, streamsConfiguration))
+                    .build())
                 .asyncSupported(true)
                 .build())
             .addServlet(ServletDetails.builder()
                 .addUrlPattern("/" + SSE_SUBPATH + "/*")
                 .servlet(servletSupport.createHttpServletBuilder(
-                    new DataStreamApplication(databindProvider, mountPointService,
-                        new RestconfDataStreamServiceImpl(scheduledThreadPool, streamsConfiguration))).build())
+                    new DataStreamApplication(databindProvider,
+                        new RestconfDataStreamServiceImpl(scheduledThreadPool, streamsConfiguration)))
+                    .build())
                 .name("notificationServlet")
                 .asyncSupported(true)
                 .build())
index 861c5f6d03effc1a14fca956ae072d817d1a3f9a..805eff2ae4ac52f1df565560a1b1c52288e2acf8 100644 (file)
@@ -33,7 +33,7 @@ public class RestconfApplication extends AbstractRestconfApplication {
             final DOMRpcService rpcService, final DOMActionService actionService,
             final DOMNotificationService notificationService, final DOMSchemaService domSchemaService,
             final StreamsConfiguration configuration) {
-        super(databindProvider, mountPointService, List.of(
+        super(databindProvider, List.of(
             streamSubscription,
             new RestconfDataServiceImpl(databindProvider, dataBroker, mountPointService, streamSubscription,
                 actionService, configuration),
index 39ae5b5a73122e04ff0bef85614a4ed6cb0110db..cb9d0d06034a65d460992b958ce6b13c8f121cc7 100644 (file)
@@ -21,7 +21,8 @@ import org.slf4j.LoggerFactory;
 /**
  * An abstract request body backed by an {@link InputStream}.
  */
-public abstract sealed class AbstractBody implements AutoCloseable permits OperationInputBody, PatchBody, ResourceBody {
+public abstract sealed class AbstractBody implements AutoCloseable
+        permits ChildBody, OperationInputBody, PatchBody, ResourceBody {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractBody.class);
 
     private static final VarHandle INPUT_STREAM;
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/ChildBody.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/ChildBody.java
new file mode 100644 (file)
index 0000000..5e37143
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.io.InputStream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
+
+public abstract sealed class ChildBody extends AbstractBody permits JsonChildBody, XmlChildBody {
+    public record PrefixAndBody(@NonNull ImmutableList<PathArgument> prefix, @NonNull NormalizedNode body) {
+        public PrefixAndBody {
+            requireNonNull(prefix);
+            requireNonNull(body);
+        }
+    }
+
+    ChildBody(final InputStream inputStream) {
+        super(inputStream);
+    }
+
+    public final @NonNull PrefixAndBody toPayload(final @NonNull YangInstanceIdentifier parentPath,
+            final @NonNull Inference parentInference) {
+        return toPayload(acquireStream(), parentPath, parentInference);
+    }
+
+    abstract @NonNull PrefixAndBody toPayload(@NonNull InputStream inputStream,
+        @NonNull YangInstanceIdentifier parentPath, @NonNull Inference parentInference);
+}
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonChildBody.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonChildBody.java
new file mode 100644 (file)
index 0000000..39a37ed
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.stream.JsonReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
+import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class JsonChildBody extends ChildBody {
+    private static final Logger LOG = LoggerFactory.getLogger(JsonChildBody.class);
+
+    public JsonChildBody(final InputStream inputStream) {
+        super(inputStream);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    PrefixAndBody toPayload(final InputStream inputStream, final YangInstanceIdentifier parentPath,
+            final Inference parentInference) {
+        NormalizedNode result;
+        try {
+            result = toNormalizedNode(inputStream, parentInference);
+        } catch (Exception e) {
+            Throwables.throwIfInstanceOf(e, RestconfDocumentedException.class);
+            LOG.debug("Error parsing json input", e);
+
+            if (e instanceof ResultAlreadySetException) {
+                throw new RestconfDocumentedException(
+                    "Error parsing json input: Failed to create new parse result data. "
+                        + "Are you creating multiple resources/subresources in POST request?", e);
+            }
+
+            RestconfDocumentedException.throwIfYangError(e);
+            throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
+                ErrorTag.MALFORMED_MESSAGE, e);
+        }
+
+        final var iiToDataList = ImmutableList.<PathArgument>builder();
+        while (result instanceof ChoiceNode choice) {
+            final var childNode = choice.body().iterator().next();
+            iiToDataList.add(result.name());
+            result = childNode;
+        }
+
+        final var resultName = result.name();
+        if (result instanceof MapEntryNode) {
+            iiToDataList.add(new NodeIdentifier(resultName.getNodeType()));
+        }
+        iiToDataList.add(resultName);
+
+        return new PrefixAndBody(iiToDataList.build(), result);
+    }
+
+    private static @NonNull NormalizedNode toNormalizedNode(final InputStream inputStream, final Inference inference) {
+        final var resultHolder = new NormalizationResultHolder();
+        final var writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
+        final var jsonParser = JsonParserStream.create(writer,
+            JSONCodecFactorySupplier.RFC7951.getShared(inference.getEffectiveModelContext()), inference);
+        final var reader = new JsonReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+        jsonParser.parse(reader);
+
+        return resultHolder.getResult().data();
+    }
+}
index ef6aa10f4d98fa1d43c5c4db13b51c3c59254ca8..53d31464056c4492ef81e9410777eeaa22815a78 100644 (file)
@@ -11,6 +11,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.PushbackInputStream;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
@@ -18,8 +20,8 @@ import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
 import org.opendaylight.yangtools.yang.model.api.stmt.ActionEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 
 /**
@@ -42,7 +44,9 @@ public abstract sealed class OperationInputBody extends AbstractBody
         try (var is = new PushbackInputStream(acquireStream())) {
             final var firstByte = is.read();
             if (firstByte == -1) {
-                return emptyInput(inference);
+                return Builders.containerBuilder()
+                    .withNodeIdentifier(new NodeIdentifier(extractInputQName(inference.toSchemaInferenceStack())))
+                    .build();
             }
             is.unread(firstByte);
 
@@ -57,20 +61,14 @@ public abstract sealed class OperationInputBody extends AbstractBody
     abstract void streamTo(@NonNull InputStream inputStream, @NonNull Inference inference,
         @NonNull NormalizedNodeStreamWriter writer) throws IOException;
 
-    private static @NonNull ContainerNode emptyInput(final Inference inference) {
-        return Builders.containerBuilder()
-            .withNodeIdentifier(new NodeIdentifier(extractInput(inference).argument()))
-            .build();
-    }
-
-    private static @NonNull InputEffectiveStatement extractInput(final Inference inference) {
-        final var stmt = inference.toSchemaInferenceStack().currentStatement();
+    static final @NonNull QName extractInputQName(final SchemaInferenceStack stack) {
+        final var stmt = stack.currentStatement();
         if (stmt instanceof RpcEffectiveStatement rpc) {
-            return rpc.input();
+            return rpc.input().argument();
         } else if (stmt instanceof ActionEffectiveStatement action) {
-            return action.streamEffectiveSubstatements(InputEffectiveStatement.class).findFirst().orElseThrow();
+            return YangConstants.operationInputQName(action.argument().getModule());
         } else {
-            throw new IllegalStateException(inference + " does not identify an 'rpc' statement");
+            throw new IllegalStateException(stack + " does not identify an 'rpc' nor an 'action' statement");
         }
     }
 }
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlChildBody.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlChildBody.java
new file mode 100644 (file)
index 0000000..c19b225
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+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.XMLNamespace;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContext;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContext.PathMixin;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+public final class XmlChildBody extends ChildBody {
+    private static final Logger LOG = LoggerFactory.getLogger(XmlChildBody.class);
+
+    public XmlChildBody(final InputStream inputStream) {
+        super(inputStream);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    PrefixAndBody toPayload(final InputStream inputStream, final YangInstanceIdentifier parentPath,
+            final Inference parentInference) {
+        try {
+            return parse(parentPath, parentInference, UntrustedXML.newDocumentBuilder().parse(inputStream));
+        } catch (final RestconfDocumentedException e) {
+            throw e;
+        } catch (final Exception e) {
+            LOG.debug("Error parsing xml input", e);
+            RestconfDocumentedException.throwIfYangError(e);
+            throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE, e);
+        }
+    }
+
+    private static @NonNull PrefixAndBody parse(final YangInstanceIdentifier path, final Inference pathInference,
+            final Document doc) throws XMLStreamException, IOException, SAXException, URISyntaxException {
+        final DataSchemaNode parentNode;
+        if (pathInference.isEmpty()) {
+            parentNode = pathInference.getEffectiveModelContext();
+        } else {
+            final var hackStack = pathInference.toSchemaInferenceStack();
+            final var hackStmt = hackStack.currentStatement();
+            if (hackStmt instanceof DataSchemaNode data) {
+                parentNode = data;
+            } else {
+                throw new IllegalStateException("Unknown SchemaNode " + hackStmt);
+            }
+        }
+
+        var schemaNode = parentNode;
+        final String docRootElm = doc.getDocumentElement().getLocalName();
+        final XMLNamespace docRootNamespace = XMLNamespace.of(doc.getDocumentElement().getNamespaceURI());
+        final var context = pathInference.getEffectiveModelContext();
+        final var it = context.findModuleStatements(docRootNamespace).iterator();
+        checkState(it.hasNext(), "Failed to find module for %s", docRootNamespace);
+        final var qname = QName.create(it.next().localQNameModule(), docRootElm);
+
+        final var iiToDataList = ImmutableList.<PathArgument>builder();
+        final var nodeAndStack = DataSchemaContextTree.from(context).enterPath(path).orElseThrow();
+        final var stack = nodeAndStack.stack();
+        var current = nodeAndStack.node();
+        do {
+            final var next = current instanceof DataSchemaContext.Composite compositeCurrent
+                ? compositeCurrent.enterChild(stack, qname) : null;
+            if (next == null) {
+                throw new IllegalStateException(
+                    "Child \"" + qname + "\" was not found in parent schema node \"" + schemaNode + "\"");
+            }
+
+            // Careful about steps: for keyed list items the individual item does not have a PathArgument step,
+            // as we do not know the key values -- we supply that later
+            final var step = next.pathStep();
+            if (step != null) {
+                iiToDataList.add(step);
+            }
+            schemaNode = next.dataSchemaNode();
+            current = next;
+        } while (current instanceof PathMixin);
+
+        final var resultHolder = new NormalizationResultHolder();
+        final var writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
+
+        final var xmlParser = XmlParserStream.create(writer, stack.toInference());
+        xmlParser.traverse(new DOMSource(doc.getDocumentElement()));
+        var parsed = resultHolder.getResult().data();
+
+        // When parsing an XML source with a list root node
+        // the new XML parser always returns a MapNode with one MapEntryNode inside.
+        // However, the old XML parser returned a MapEntryNode directly in this place.
+        // Therefore we now have to extract the MapEntryNode from the parsed MapNode.
+        if (parsed instanceof MapNode mapNode) {
+            // extracting the MapEntryNode
+            parsed = mapNode.body().iterator().next();
+        }
+
+        if (schemaNode instanceof ListSchemaNode) {
+            // Supply the last item
+            iiToDataList.add(parsed.name());
+        }
+
+        return new PrefixAndBody(iiToDataList.build(), parsed);
+    }
+}
index 74d696787fc683970cd0f29777bd0284e6a74d33..798a299fc41829e191ea7a3a4405b6f909080eae 100644 (file)
@@ -16,7 +16,6 @@ import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
-import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,11 +32,7 @@ public final class XmlOperationInputBody extends OperationInputBody {
             throws IOException {
         // Adjust inference to point to input
         final var stack = inference.toSchemaInferenceStack();
-        if (stack.currentStatement() instanceof RpcEffectiveStatement rpcStmt) {
-            stack.enterSchemaTree(rpcStmt.input().argument());
-        } else {
-            throw new IllegalStateException(inference + " does not identify an 'rpc' statement");
-        }
+        stack.enterDataTree(extractInputQName(stack));
 
         try {
             XmlParserStream.create(writer, stack.toInference()).parse(UntrustedXML.createXMLStreamReader(inputStream));
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/AbstractNormalizedNodeBodyReader.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/AbstractNormalizedNodeBodyReader.java
deleted file mode 100644 (file)
index ba26894..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.VisibleForTesting;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PushbackInputStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.UriInfo;
-import javax.ws.rs.ext.MessageBodyReader;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
-import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
-
-/**
- * Common superclass for readers producing {@link NormalizedNodePayload}.
- */
-@VisibleForTesting
-public abstract class AbstractNormalizedNodeBodyReader implements MessageBodyReader<NormalizedNodePayload> {
-    private final DatabindProvider databindProvider;
-    private final DOMMountPointService mountPointService;
-
-    @Context
-    private UriInfo uriInfo;
-
-    AbstractNormalizedNodeBodyReader(final DatabindProvider databindProvider,
-            final DOMMountPointService mountPointService) {
-        this.databindProvider = requireNonNull(databindProvider);
-        this.mountPointService = mountPointService;
-    }
-
-    @Override
-    public final boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations,
-            final MediaType mediaType) {
-        return true;
-    }
-
-    @Override
-    public final NormalizedNodePayload readFrom(final Class<NormalizedNodePayload> type, final Type genericType,
-            final Annotation[] annotations, final MediaType mediaType,
-            final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws IOException,
-            WebApplicationException {
-        final InstanceIdentifierContext path = ParserIdentifier.toInstanceIdentifier(
-            uriInfo.getPathParameters(false).getFirst("identifier"), databindProvider.currentContext().modelContext(),
-            mountPointService);
-
-        final PushbackInputStream pushbackInputStream = new PushbackInputStream(entityStream);
-
-        int firstByte = pushbackInputStream.read();
-        if (firstByte == -1) {
-            return NormalizedNodePayload.empty(path);
-        } else {
-            pushbackInputStream.unread(firstByte);
-            return readBody(path, pushbackInputStream);
-        }
-    }
-
-    protected abstract NormalizedNodePayload readBody(InstanceIdentifierContext path, InputStream entityStream)
-        throws WebApplicationException;
-
-    @VisibleForTesting
-    public final void setUriInfo(final UriInfo uriInfo) {
-        this.uriInfo = uriInfo;
-    }
-}
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/JsonNormalizedNodeBodyReader.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/JsonNormalizedNodeBodyReader.java
deleted file mode 100644 (file)
index 1e152d1..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import com.google.common.base.Throwables;
-import com.google.gson.stream.JsonReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.ext.Provider;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
-import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.nb.rfc8040.MediaTypes;
-import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.yangtools.yang.common.ErrorTag;
-import org.opendaylight.yangtools.yang.common.ErrorType;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
-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.codec.gson.JSONCodecFactorySupplier;
-import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
-import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException;
-import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Provider
-@Consumes({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
-public class JsonNormalizedNodeBodyReader extends AbstractNormalizedNodeBodyReader {
-    private static final Logger LOG = LoggerFactory.getLogger(JsonNormalizedNodeBodyReader.class);
-
-    public JsonNormalizedNodeBodyReader(final DatabindProvider databindProvider,
-            final DOMMountPointService mountPointService) {
-        super(databindProvider, mountPointService);
-    }
-
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    @Override
-    protected NormalizedNodePayload readBody(final InstanceIdentifierContext path, final InputStream entityStream)
-            throws WebApplicationException {
-        try {
-            return readFrom(path, entityStream);
-        } catch (final Exception e) {
-            propagateExceptionAs(e);
-            return null;
-        }
-    }
-
-    public static NormalizedNodePayload readFrom(final InstanceIdentifierContext path, final InputStream entityStream) {
-        final NormalizationResultHolder resultHolder = new NormalizationResultHolder();
-        final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
-
-        final var parentSchema = path.inference();
-
-        final JsonParserStream jsonParser = JsonParserStream.create(writer,
-            JSONCodecFactorySupplier.RFC7951.getShared(path.getSchemaContext()), parentSchema);
-
-        final JsonReader reader = new JsonReader(new InputStreamReader(entityStream, StandardCharsets.UTF_8));
-        jsonParser.parse(reader);
-
-        NormalizedNode result = resultHolder.getResult().data();
-        final List<YangInstanceIdentifier.PathArgument> iiToDataList = new ArrayList<>();
-
-        while (result instanceof ChoiceNode choice) {
-            final var childNode = choice.body().iterator().next();
-            iiToDataList.add(result.name());
-            result = childNode;
-        }
-
-        if (result instanceof MapEntryNode) {
-            iiToDataList.add(new NodeIdentifier(result.name().getNodeType()));
-            iiToDataList.add(result.name());
-        } else {
-            final var parentPath = parentSchema.statementPath();
-            if (parentPath.isEmpty() || !(parentPath.get(parentPath.size() - 1) instanceof OperationDefinition)) {
-                iiToDataList.add(result.name());
-            }
-        }
-
-        // FIXME: can result really be null?
-        return NormalizedNodePayload.ofNullable(path.withConcatenatedArgs(iiToDataList), result);
-    }
-
-    private static void propagateExceptionAs(final Exception exception) throws RestconfDocumentedException {
-        Throwables.throwIfInstanceOf(exception, RestconfDocumentedException.class);
-        LOG.debug("Error parsing json input", exception);
-
-        if (exception instanceof ResultAlreadySetException) {
-            throw new RestconfDocumentedException("Error parsing json input: Failed to create new parse result data. "
-                    + "Are you creating multiple resources/subresources in POST request?", exception);
-        }
-
-        RestconfDocumentedException.throwIfYangError(exception);
-        throw new RestconfDocumentedException("Error parsing input: " + exception.getMessage(), ErrorType.PROTOCOL,
-            ErrorTag.MALFORMED_MESSAGE, exception);
-    }
-}
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlNormalizedNodeBodyReader.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlNormalizedNodeBodyReader.java
deleted file mode 100644 (file)
index b83145a..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.ext.Provider;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.transform.dom.DOMSource;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
-import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.nb.rfc8040.MediaTypes;
-import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.yangtools.util.xml.UntrustedXML;
-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.XMLNamespace;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContext;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContext.PathMixin;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
-import org.opendaylight.yangtools.yang.model.api.ContainerLike;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-@Provider
-@Consumes({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-public class XmlNormalizedNodeBodyReader extends AbstractNormalizedNodeBodyReader {
-    private static final Logger LOG = LoggerFactory.getLogger(XmlNormalizedNodeBodyReader.class);
-
-    public XmlNormalizedNodeBodyReader(final DatabindProvider databindProvider,
-            final DOMMountPointService mountPointService) {
-        super(databindProvider, mountPointService);
-    }
-
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    @Override
-    protected NormalizedNodePayload readBody(final InstanceIdentifierContext path, final InputStream entityStream)
-            throws WebApplicationException {
-        try {
-            return parse(path, UntrustedXML.newDocumentBuilder().parse(entityStream));
-        } catch (final RestconfDocumentedException e) {
-            throw e;
-        } catch (final Exception e) {
-            LOG.debug("Error parsing xml input", e);
-            RestconfDocumentedException.throwIfYangError(e);
-            throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
-                    ErrorTag.MALFORMED_MESSAGE, e);
-        }
-    }
-
-    private static NormalizedNodePayload parse(final InstanceIdentifierContext pathContext, final Document doc)
-            throws XMLStreamException, IOException, SAXException, URISyntaxException {
-        final SchemaNode schemaNodeContext = pathContext.getSchemaNode();
-        DataSchemaNode schemaNode;
-        final List<PathArgument> iiToDataList = new ArrayList<>();
-        Inference inference;
-        if (schemaNodeContext instanceof OperationDefinition oper) {
-            schemaNode = oper.getInput();
-
-            final var stack = pathContext.inference().toSchemaInferenceStack();
-            stack.enterSchemaTree(schemaNode.getQName());
-            inference = stack.toInference();
-        } else if (schemaNodeContext instanceof DataSchemaNode data) {
-            schemaNode = data;
-
-            final String docRootElm = doc.getDocumentElement().getLocalName();
-            final XMLNamespace docRootNamespace = XMLNamespace.of(doc.getDocumentElement().getNamespaceURI());
-            final var context = pathContext.getSchemaContext();
-            final var it = context.findModuleStatements(docRootNamespace).iterator();
-            checkState(it.hasNext(), "Failed to find module for %s", docRootNamespace);
-            final var qname = QName.create(it.next().localQNameModule(), docRootElm);
-
-            final var nodeAndStack = DataSchemaContextTree.from(context)
-                .enterPath(pathContext.getInstanceIdentifier()).orElseThrow();
-
-            final var stack = nodeAndStack.stack();
-            var current = nodeAndStack.node();
-            do {
-                final var next = current instanceof DataSchemaContext.Composite compositeCurrent
-                    ? compositeCurrent.enterChild(stack, qname) : null;
-                if (next == null) {
-                    throw new IllegalStateException(
-                        "Child \"" + qname + "\" was not found in parent schema node \"" + schemaNode + "\"");
-                }
-
-                // Careful about steps: for keyed list items the individual item does not have a PathArgument step,
-                // as we do not know the key values -- we supply that later
-                final var step = next.pathStep();
-                if (step != null) {
-                    iiToDataList.add(step);
-                }
-                schemaNode = next.dataSchemaNode();
-                current = next;
-            } while (current instanceof PathMixin);
-
-            inference = stack.toInference();
-        } else {
-            throw new IllegalStateException("Unknown SchemaNode " + schemaNodeContext);
-        }
-
-        NormalizedNode parsed;
-        final NormalizationResultHolder resultHolder = new NormalizationResultHolder();
-        final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
-
-        if (schemaNode instanceof ContainerLike || schemaNode instanceof ListSchemaNode
-                || schemaNode instanceof LeafSchemaNode) {
-            final XmlParserStream xmlParser = XmlParserStream.create(writer, inference);
-            xmlParser.traverse(new DOMSource(doc.getDocumentElement()));
-            parsed = resultHolder.getResult().data();
-
-            // When parsing an XML source with a list root node
-            // the new XML parser always returns a MapNode with one MapEntryNode inside.
-            // However, the old XML parser returned a MapEntryNode directly in this place.
-            // Therefore we now have to extract the MapEntryNode from the parsed MapNode.
-            if (parsed instanceof MapNode mapNode) {
-                // extracting the MapEntryNode
-                parsed = mapNode.body().iterator().next();
-            }
-
-            if (schemaNode instanceof ListSchemaNode) {
-                // Supply the last item
-                iiToDataList.add(parsed.name());
-            }
-        } else {
-            LOG.warn("Unknown schema node extension {} was not parsed", schemaNode.getClass());
-            parsed = null;
-        }
-
-        // FIXME: can result really be null?
-        return NormalizedNodePayload.ofNullable(pathContext.withConcatenatedArgs(iiToDataList), parsed);
-    }
-}
-
index 0e79529b849034ba53403ccc136d5514f69ae2eb..660a837b59daae4861ffd2affd2fa54e6608a79a 100644 (file)
@@ -63,11 +63,17 @@ import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.nb.rfc8040.MediaTypes;
 import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
+import org.opendaylight.restconf.nb.rfc8040.databind.ChildBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
+import org.opendaylight.restconf.nb.rfc8040.databind.JsonChildBody;
+import org.opendaylight.restconf.nb.rfc8040.databind.JsonOperationInputBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.JsonPatchBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.JsonResourceBody;
+import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.PatchBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.ResourceBody;
+import org.opendaylight.restconf.nb.rfc8040.databind.XmlChildBody;
+import org.opendaylight.restconf.nb.rfc8040.databind.XmlOperationInputBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.XmlPatchBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.XmlResourceBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
@@ -87,7 +93,6 @@ import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenersBroker;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev170126.restconf.restconf.Data;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
@@ -379,11 +384,30 @@ public final class RestconfDataServiceImpl {
         };
     }
 
+    /**
+     * Create a top-level data resource.
+     *
+     * @param body data node for put to config DS
+     * @param uriInfo URI info
+     * @return {@link Response}
+     */
+    @POST
+    @Path("/data/{identifier:.+}")
+    @Consumes({
+        MediaTypes.APPLICATION_YANG_DATA_JSON,
+        MediaType.APPLICATION_JSON,
+    })
+    public Response postDataJSON(final InputStream body, @Context final UriInfo uriInfo) {
+        try (var jsonBody = new JsonChildBody(body)) {
+            return postData(jsonBody, uriInfo);
+        }
+    }
+
     /**
      * Create a data resource in target.
      *
      * @param identifier path to target
-     * @param payload new data
+     * @param body data node for put to config DS
      * @param uriInfo URI info
      * @return {@link Response}
      */
@@ -391,44 +415,89 @@ public final class RestconfDataServiceImpl {
     @Path("/data/{identifier:.+}")
     @Consumes({
         MediaTypes.APPLICATION_YANG_DATA_JSON,
-        MediaTypes.APPLICATION_YANG_DATA_XML,
         MediaType.APPLICATION_JSON,
+    })
+    public Response postDataJSON(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
+            @Context final UriInfo uriInfo) {
+        final var instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
+            databindProvider.currentContext().modelContext(), mountPointService);
+        if (instanceIdentifier.getSchemaNode() instanceof ActionDefinition) {
+            try (var jsonBody = new JsonOperationInputBody(body)) {
+                return invokeAction(instanceIdentifier, jsonBody);
+            }
+        }
+
+        try (var jsonBody = new JsonChildBody(body)) {
+            return postData(instanceIdentifier, jsonBody, uriInfo);
+        }
+    }
+
+    /**
+     * Create a top-level data resource.
+     *
+     * @param body data node for put to config DS
+     * @param uriInfo URI info
+     * @return {@link Response}
+     */
+    @POST
+    @Path("/data")
+    @Consumes({
+        MediaTypes.APPLICATION_YANG_DATA_XML,
         MediaType.APPLICATION_XML,
         MediaType.TEXT_XML
     })
-    public Response postData(@Encoded @PathParam("identifier") final String identifier,
-            final NormalizedNodePayload payload, @Context final UriInfo uriInfo) {
-        return postData(payload, uriInfo);
+    public Response postDataXML(final InputStream body, @Context final UriInfo uriInfo) {
+        try (var xmlBody = new XmlChildBody(body)) {
+            return postData(xmlBody, uriInfo);
+        }
     }
 
     /**
-     * Create a data resource.
+     * Create a data resource in target.
      *
-     * @param payload new data
+     * @param identifier path to target
+     * @param body data node for put to config DS
      * @param uriInfo URI info
      * @return {@link Response}
      */
     @POST
-    @Path("/data")
+    @Path("/data/{identifier:.+}")
     @Consumes({
-        MediaTypes.APPLICATION_YANG_DATA_JSON,
         MediaTypes.APPLICATION_YANG_DATA_XML,
-        MediaType.APPLICATION_JSON,
         MediaType.APPLICATION_XML,
         MediaType.TEXT_XML
     })
-    public Response postData(final NormalizedNodePayload payload, @Context final UriInfo uriInfo) {
-        requireNonNull(payload);
-        final var iid = payload.getInstanceIdentifierContext();
-        if (iid.getSchemaNode() instanceof ActionDefinition) {
-            return invokeAction(payload);
+    public Response postDataXML(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
+            @Context final UriInfo uriInfo) {
+        final var instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
+            databindProvider.currentContext().modelContext(), mountPointService);
+        if (instanceIdentifier.getSchemaNode() instanceof ActionDefinition) {
+            try (var xmlBody = new XmlOperationInputBody(body)) {
+                return invokeAction(instanceIdentifier, xmlBody);
+            }
         }
 
+        try (var xmlBody = new XmlChildBody(body)) {
+            return postData(instanceIdentifier, xmlBody, uriInfo);
+        }
+    }
+
+    private Response postData(final ChildBody body, final UriInfo uriInfo) {
+        return postData(InstanceIdentifierContext.ofLocalRoot(databindProvider.currentContext().modelContext()), body,
+            uriInfo);
+    }
+
+    private Response postData(final InstanceIdentifierContext iid, final ChildBody body, final UriInfo uriInfo) {
         final var params = QueryParams.newWriteDataParams(uriInfo);
         final var strategy = getRestconfStrategy(iid.getMountPoint());
-        final var path = iid.getInstanceIdentifier();
         final var context = iid.getSchemaContext();
-        final var data = payload.getData();
+        var path = iid.getInstanceIdentifier();
+        final var payload = body.toPayload(path, iid.inference());
+        final var data = payload.body();
+
+        for (var arg : payload.prefix()) {
+            path = path.node(arg);
+        }
 
         PostDataTransactionUtil.postData(path, data, strategy, context, params);
         return Response.created(resolveLocation(uriInfo, path, context, data)).build();
@@ -731,38 +800,28 @@ public final class RestconfDataServiceImpl {
      * @param payload {@link NormalizedNodePayload} - the body of the operation
      * @return {@link NormalizedNodePayload} wrapped in {@link Response}
      */
-    public Response invokeAction(final NormalizedNodePayload payload) {
-        final InstanceIdentifierContext context = payload.getInstanceIdentifierContext();
-        final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier();
-        final NormalizedNode data = payload.getData();
-
-        if (yangIIdContext.isEmpty() && !Data.QNAME.equals(data.name().getNodeType())) {
-            throw new RestconfDocumentedException("Instance identifier need to contain at least one path argument",
-                ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
-        }
-
-        final DOMMountPoint mountPoint = context.getMountPoint();
-        final Absolute schemaPath = context.inference().toSchemaInferenceStack().toSchemaNodeIdentifier();
-        final DOMActionResult response;
-        if (mountPoint != null) {
-            response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, mountPoint);
-        } else {
-            response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, actionService);
+    private Response invokeAction(final InstanceIdentifierContext context, final OperationInputBody body) {
+        final var yangIIdContext = context.getInstanceIdentifier();
+        final ContainerNode input;
+        try {
+            input = body.toContainerNode(context.inference());
+        } catch (IOException e) {
+            LOG.debug("Error reading input", e);
+            throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE, e);
         }
-        final DOMActionResult result = checkActionResponse(response);
 
-        ContainerNode resultData = null;
-        if (result != null) {
-            resultData = result.getOutput().orElse(null);
-        }
+        final var mountPoint = context.getMountPoint();
+        final var schemaPath = context.inference().toSchemaInferenceStack().toSchemaNodeIdentifier();
+        final var response = mountPoint != null ? invokeAction(input, schemaPath, yangIIdContext, mountPoint)
+            : invokeAction(input, schemaPath, yangIIdContext, actionService);
+        final var result = checkActionResponse(response);
 
+        final var resultData = result != null ? result.getOutput().orElse(null) : null;
         if (resultData != null && resultData.isEmpty()) {
             return Response.status(Status.NO_CONTENT).build();
         }
-
-        return Response.status(Status.OK)
-            .entity(NormalizedNodePayload.ofNullable(context, resultData))
-            .build();
+        return Response.status(Status.OK).entity(NormalizedNodePayload.ofNullable(context, resultData)).build();
     }
 
     /**
index 29b916051c4bdcda5b5851597eeb4f0bcccb53df..2255773d1c8fefd9e830e1ed8ceb818a7e65465c 100644 (file)
@@ -23,6 +23,11 @@ public abstract class AbstractInstanceIdentifierTest {
     protected static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = QNameModule.create(
         XMLNamespace.of("instance:identifier:module"), Revision.of("2014-01-17"));
 
+    protected static final QName CONT_QNAME = QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont");
+    protected static final QName CONT1_QNAME = QName.create(CONT_QNAME, "cont1");
+    protected static final QName RESET_QNAME = QName.create(CONT_QNAME, "reset");
+    protected static final QName DELAY_QNAME = QName.create(CONT_QNAME, "delay");
+
     protected static final QName CASE_LEAF1_QNAME = QName.create("choice:ns", "case-leaf1");
     protected static final QName CHOICE_CONT_QNAME = QName.create("choice:ns", "case-cont1");
 
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractBodyTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractBodyTest.java
new file mode 100644 (file)
index 0000000..a2455f8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.restconf.nb.rfc8040.AbstractInstanceIdentifierTest;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+
+public abstract class AbstractBodyTest extends AbstractInstanceIdentifierTest {
+    static final List<File> loadFiles(final String resourceDirectory) throws FileNotFoundException {
+        final String path = AbstractBodyTest.class.getResource(resourceDirectory).getPath();
+        final File testDir = new File(path);
+        final String[] fileList = testDir.list();
+        final List<File> testFiles = new ArrayList<>();
+        if (fileList == null) {
+            throw new FileNotFoundException(resourceDirectory);
+        }
+        for (final String fileName : fileList) {
+            if (fileName.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION)
+                && !new File(testDir, fileName).isDirectory()) {
+                testFiles.add(new File(testDir, fileName));
+            }
+        }
+        return testFiles;
+    }
+}
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractOperationInputBodyTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/AbstractOperationInputBodyTest.java
new file mode 100644 (file)
index 0000000..5130c56
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.restconf.nb.rfc8040.AbstractInstanceIdentifierTest;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Uint32;
+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.model.util.SchemaInferenceStack;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
+
+abstract class AbstractOperationInputBodyTest extends AbstractInstanceIdentifierTest {
+    private static final NodeIdentifier INPUT_NID = new NodeIdentifier(QName.create(CONT_QNAME, "input"));
+
+    private static Inference RESET_INFERENCE;
+
+    @BeforeClass
+    public static final void setupInference() {
+        final var stack = SchemaInferenceStack.ofDataTreePath(IID_SCHEMA, CONT_QNAME, CONT1_QNAME);
+        stack.enterSchemaTree(RESET_QNAME);
+        RESET_INFERENCE = stack.toInference();
+    }
+
+    @Test
+    public final void moduleSubContainerDataPostActionTest() throws Exception {
+        final var body = moduleSubContainerDataPostActionBody();
+
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(INPUT_NID)
+            .withChild(ImmutableNodes.leafNode(DELAY_QNAME, Uint32.valueOf(600)))
+            .build(), body.toContainerNode(RESET_INFERENCE));
+    }
+
+    abstract OperationInputBody moduleSubContainerDataPostActionBody();
+
+    @Test
+    public final void testEmpty() throws Exception {
+        final var body = testEmptyBody();
+        assertEquals(Builders.containerBuilder().withNodeIdentifier(INPUT_NID).build(),
+            body.toContainerNode(RESET_INFERENCE));
+    }
+
+    abstract OperationInputBody testEmptyBody();
+}
index 484d45f292d1a2c782d3e18c72161b646d6e4cf4..0b812620147b9476f7a5dd1e444af06b725dce82 100644 (file)
@@ -8,21 +8,36 @@
 package org.opendaylight.restconf.nb.rfc8040.databind;
 
 import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Optional;
 import java.util.function.Function;
+import javax.ws.rs.core.Response.Status;
 import org.eclipse.jdt.annotation.NonNull;
 import org.junit.BeforeClass;
-import org.opendaylight.restconf.nb.rfc8040.jersey.providers.AbstractBodyReaderTest;
+import org.junit.function.ThrowingRunnable;
+import org.opendaylight.mdsal.dom.api.DOMMountPoint;
+import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
+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.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
-abstract class AbstractResourceBodyTest extends AbstractBodyReaderTest {
+abstract class AbstractResourceBodyTest extends AbstractBodyTest {
     static final NodeIdentifier CONT_NID = new NodeIdentifier(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
     static final NodeIdentifier CONT1_NID = new NodeIdentifier(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont1"));
 
@@ -39,9 +54,18 @@ abstract class AbstractResourceBodyTest extends AbstractBodyReaderTest {
     static EffectiveModelContext MODEL_CONTEXT;
 
     private final Function<InputStream, ResourceBody> bodyConstructor;
+    private final DOMMountPointService mountPointService;
+    private final DOMMountPoint mountPoint;
 
     AbstractResourceBodyTest(final Function<InputStream, ResourceBody> bodyConstructor) {
         this.bodyConstructor = requireNonNull(bodyConstructor);
+
+        mountPointService = mock(DOMMountPointService.class);
+        mountPoint = mock(DOMMountPoint.class);
+        doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(any(YangInstanceIdentifier.class));
+        doReturn(Optional.of(FixedDOMSchemaService.of(IID_SCHEMA))).when(mountPoint)
+            .getService(DOMSchemaService.class);
+
     }
 
     @BeforeClass
@@ -68,4 +92,18 @@ abstract class AbstractResourceBodyTest extends AbstractBodyReaderTest {
             return body.toNormalizedNode(context.getInstanceIdentifier(), context.inference(), context.getSchemaNode());
         }
     }
+
+    static final void assertRangeViolation(final ThrowingRunnable runnable) {
+        final var ex = assertThrows(RestconfDocumentedException.class, runnable);
+        assertEquals(Status.BAD_REQUEST, ex.getResponse().getStatusInfo());
+
+        final var errors = ex.getErrors();
+        assertEquals(1, errors.size());
+
+        final var error = errors.get(0);
+        assertEquals(ErrorType.APPLICATION, error.getErrorType());
+        assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
+        assertEquals("bar error app tag", error.getErrorAppTag());
+        assertEquals("bar error message", error.getErrorMessage());
+    }
 }
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonChildBodyTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonChildBodyTest.java
new file mode 100644 (file)
index 0000000..32f89de
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class JsonChildBodyTest extends AbstractBodyTest {
+    private static EffectiveModelContext schemaContext;
+
+    @BeforeClass
+    public static void initialization() throws Exception {
+        final var testFiles = loadFiles("/instanceidentifier/yang");
+        testFiles.addAll(loadFiles("/modules"));
+        schemaContext = YangParserTestUtils.parseYangFiles(testFiles);
+    }
+
+    @Test
+    public void moduleSubContainerDataPostTest() throws Exception {
+        final var body = new JsonChildBody(
+            JsonChildBodyTest.class.getResourceAsStream("/instanceidentifier/json/json_sub_container.json"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(CONT_QNAME),
+            Inference.ofDataTreePath(schemaContext, CONT_QNAME));
+
+        final var lflst11 = QName.create("augment:module:leaf:list", "2014-01-27", "lflst11");
+        assertEquals(List.of(new NodeIdentifier(CONT1_QNAME)), payload.prefix());
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(CONT1_QNAME))
+            .withChild(ImmutableNodes.leafNode(QName.create("augment:module:leaf:list", "2014-01-27", "lf11"),
+                YangInstanceIdentifier.of(
+                    new NodeIdentifier(CONT_QNAME),
+                    new NodeIdentifier(CONT1_QNAME),
+                    new NodeIdentifier(lflst11),
+                    new NodeWithValue<>(lflst11, "lflst11_1"))))
+            .build(), payload.body());
+    }
+
+    @Test
+    public void moduleSubContainerAugmentDataPostTest() throws Exception {
+        final var body = new JsonChildBody(
+            JsonChildBodyTest.class.getResourceAsStream("/instanceidentifier/json/json_augment_container.json"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(CONT_QNAME),
+            Inference.ofDataTreePath(schemaContext, CONT_QNAME));
+
+        final var contAugment = QName.create("augment:module", "2014-01-17", "cont-augment");
+        assertEquals(List.of(new NodeIdentifier(contAugment)), payload.prefix());
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(contAugment))
+            .withChild(ImmutableNodes.leafNode(QName.create(contAugment, "leaf1"), "stryng"))
+            .build(), payload.body());
+    }
+
+    @Test
+    public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception {
+        final var body = new JsonChildBody(
+            JsonChildBodyTest.class.getResourceAsStream("/instanceidentifier/json/json_augment_choice_container.json"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(new NodeIdentifier(CONT_QNAME)),
+            Inference.ofDataTreePath(schemaContext, CONT_QNAME));
+
+        final var container1 = QName.create("augment:module", "2014-01-17", "case-choice-case-container1");
+        assertEquals(List.of(
+            new NodeIdentifier(QName.create(container1, "augment-choice1")),
+            new NodeIdentifier(QName.create(container1, "augment-choice2")),
+            new NodeIdentifier(container1)), payload.prefix());
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(container1))
+            .withChild(ImmutableNodes.leafNode(QName.create(container1, "case-choice-case-leaf1"), "stryng"))
+            .build(), payload.body());
+    }
+}
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonOperationInputBodyTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/JsonOperationInputBodyTest.java
new file mode 100644 (file)
index 0000000..e8a85e6
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import java.io.InputStream;
+
+public class JsonOperationInputBodyTest extends AbstractOperationInputBodyTest {
+    @Override
+    OperationInputBody moduleSubContainerDataPostActionBody() {
+        return new JsonOperationInputBody(stringInputStream("""
+            {
+              "instance-identifier-module:input": {
+                "delay": 600
+              }
+            }"""));
+    }
+
+    @Override
+    OperationInputBody testEmptyBody() {
+        return new JsonOperationInputBody(InputStream.nullInputStream());
+    }
+}
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlChildBodyTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlChildBodyTest.java
new file mode 100644 (file)
index 0000000..e845f1b
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class XmlChildBodyTest extends AbstractBodyTest {
+    private static final QName TOP_LEVEL_LIST = QName.create("foo", "2017-08-09", "top-level-list");
+
+    private static EffectiveModelContext schemaContext;
+
+    @BeforeClass
+    public static void initialization() throws Exception {
+        final var testFiles = loadFiles("/instanceidentifier/yang");
+        testFiles.addAll(loadFiles("/modules"));
+        testFiles.addAll(loadFiles("/foo-xml-test/yang"));
+        schemaContext = YangParserTestUtils.parseYangFiles(testFiles);
+    }
+
+    @Test
+    public void postXmlTest() throws Exception {
+        final var body = new XmlChildBody(XmlChildBodyTest.class.getResourceAsStream("/foo-xml-test/foo.xml"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(), Inference.ofDataTreePath(schemaContext));
+
+        final var entryId = NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST,
+            QName.create(TOP_LEVEL_LIST, "key-leaf"), "key-value");
+        assertEquals(List.of(new NodeIdentifier(TOP_LEVEL_LIST), entryId), payload.prefix());
+        assertEquals(Builders.mapEntryBuilder()
+            .withNodeIdentifier(entryId)
+            .withChild(ImmutableNodes.leafNode(QName.create(TOP_LEVEL_LIST, "key-leaf"), "key-value"))
+            .withChild(ImmutableNodes.leafNode(QName.create(TOP_LEVEL_LIST, "ordinary-leaf"), "leaf-value"))
+            .build(), payload.body());
+    }
+
+    @Test
+    public void moduleSubContainerDataPostTest() throws Exception {
+        final var body = new XmlChildBody(
+            XmlChildBodyTest.class.getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(CONT_QNAME),
+            Inference.ofDataTreePath(schemaContext, CONT_QNAME));
+
+        final var lflst11 = QName.create("augment:module:leaf:list", "2014-01-27", "lflst11");
+        assertEquals(List.of(new NodeIdentifier(CONT1_QNAME)), payload.prefix());
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(CONT1_QNAME))
+            .withChild(Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(lflst11))
+                .withChildValue("lflst11_1")
+                .withChildValue("lflst11_2")
+                .withChildValue("lflst11_3")
+                .build())
+            .withChild(ImmutableNodes.leafNode(QName.create(lflst11, "lf11"), YangInstanceIdentifier.of(
+                new NodeIdentifier(CONT_QNAME),
+                new NodeIdentifier(CONT1_QNAME),
+                new NodeIdentifier(lflst11),
+                new NodeWithValue<>(lflst11, "lflst11_1"))))
+            .build(), payload.body());
+    }
+
+    @Test
+    public void moduleSubContainerAugmentDataPostTest() throws Exception {
+        final var body = new XmlChildBody(
+            XmlChildBodyTest.class.getResourceAsStream("/instanceidentifier/xml/xml_augment_container.xml"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(CONT_QNAME),
+            Inference.ofDataTreePath(schemaContext, CONT_QNAME));
+
+        final var contAugment = QName.create("augment:module", "2014-01-17", "cont-augment");
+        assertEquals(List.of(new NodeIdentifier(contAugment)), payload.prefix());
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(contAugment))
+            .withChild(ImmutableNodes.leafNode(QName.create(contAugment, "leaf1"), "stryng"))
+            .build(), payload.body());
+    }
+
+    @Test
+    public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception {
+        final var body = new XmlChildBody(
+            XmlChildBodyTest.class.getResourceAsStream("/instanceidentifier/xml/xml_augment_choice_container.xml"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(CONT_QNAME),
+            Inference.ofDataTreePath(schemaContext, CONT_QNAME));
+
+        final var container1 = QName.create("augment:module", "2014-01-17", "case-choice-case-container1");
+        assertEquals(List.of(
+            new NodeIdentifier(QName.create(container1, "augment-choice1")),
+            new NodeIdentifier(QName.create(container1, "augment-choice2")),
+            new NodeIdentifier(container1)), payload.prefix());
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(container1))
+            .withChild(ImmutableNodes.leafNode(QName.create(container1, "case-choice-case-leaf1"), "stryng"))
+            .build(), payload.body());
+    }
+
+    /**
+     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
+     * used to distinguish between them to find correct one. Check if container was found not only according to its
+     * name, but also by correct namespace used in payload.
+     */
+    @Test
+    public void findFooContainerUsingNamespaceTest() throws Exception {
+        final var body = new XmlChildBody(
+            XmlChildBodyTest.class.getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(), Inference.ofDataTreePath(schemaContext));
+
+        final var fooBarContainer = new NodeIdentifier(QName.create("foo:module", "2016-09-29", "foo-bar-container"));
+        assertEquals(List.of(fooBarContainer), payload.prefix());
+        assertEquals(Builders.containerBuilder().withNodeIdentifier(fooBarContainer).build(),
+            payload.body());
+    }
+
+    /**
+     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
+     * used to distinguish between them to find correct one. Check if container was found not only according to its
+     * name, but also by correct namespace used in payload.
+     */
+    @Test
+    public void findBarContainerUsingNamespaceTest() throws Exception {
+        final var body = new XmlChildBody(
+            XmlChildBodyTest.class.getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml"));
+        final var payload = body.toPayload(YangInstanceIdentifier.of(), Inference.ofDataTreePath(schemaContext));
+
+        final var fooBarContainer = new NodeIdentifier(QName.create("bar:module", "2016-09-29", "foo-bar-container"));
+        assertEquals(List.of(fooBarContainer), payload.prefix());
+        assertEquals(Builders.containerBuilder().withNodeIdentifier(fooBarContainer).build(),
+            payload.body());
+    }
+}
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlOperationInputBodyTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/XmlOperationInputBodyTest.java
new file mode 100644 (file)
index 0000000..e93ae29
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.databind;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.InputStream;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.model.util.SchemaInferenceStack;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class XmlOperationInputBodyTest extends AbstractOperationInputBodyTest {
+    @Override
+    OperationInputBody moduleSubContainerDataPostActionBody() {
+        return new XmlOperationInputBody(stringInputStream("""
+            <input xmlns="instance:identifier:module">
+              <delay>600</delay>
+            </input>"""));
+    }
+
+    @Override
+    OperationInputBody testEmptyBody() {
+        return new XmlOperationInputBody(InputStream.nullInputStream());
+    }
+
+    @Test
+    public void rpcModuleInputTest() throws Exception {
+        final var rpcTest = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test");
+        final var stack = SchemaInferenceStack.of(YangParserTestUtils.parseYangResourceDirectory("/invoke-rpc"));
+        stack.enterSchemaTree(rpcTest);
+
+        final var body = new XmlOperationInputBody(
+            XmlOperationInputBodyTest.class.getResourceAsStream("/invoke-rpc/xml/rpc-input.xml"));
+
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(QName.create(rpcTest, "input")))
+            .withChild(Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(rpcTest, "cont")))
+                .withChild(ImmutableNodes.leafNode(QName.create(rpcTest, "lf"), "lf-test"))
+                .build())
+            .build(), body.toContainerNode(stack.toInference()));
+    }
+}
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/AbstractBodyReaderTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/AbstractBodyReaderTest.java
deleted file mode 100644 (file)
index 7e3b504..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThrows;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import javax.ws.rs.core.MultivaluedHashMap;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriInfo;
-import org.junit.function.ThrowingRunnable;
-import org.opendaylight.mdsal.dom.api.DOMMountPoint;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
-import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.common.errors.RestconfError;
-import org.opendaylight.restconf.nb.rfc8040.AbstractInstanceIdentifierTest;
-import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
-import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.yangtools.yang.common.ErrorTag;
-import org.opendaylight.yangtools.yang.common.ErrorType;
-import org.opendaylight.yangtools.yang.common.YangConstants;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-
-public abstract class AbstractBodyReaderTest extends AbstractInstanceIdentifierTest {
-    protected final DatabindProvider databindProvider;
-    protected final DOMMountPointService mountPointService;
-    protected final DOMMountPoint mountPoint;
-
-    protected AbstractBodyReaderTest() {
-        this(IID_SCHEMA);
-    }
-
-    protected AbstractBodyReaderTest(final EffectiveModelContext schemaContext) {
-        final var databindContext = DatabindContext.ofModel(schemaContext);
-        databindProvider = () -> databindContext;
-
-        mountPointService = mock(DOMMountPointService.class);
-        mountPoint = mock(DOMMountPoint.class);
-        doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(any(YangInstanceIdentifier.class));
-        doReturn(Optional.of(FixedDOMSchemaService.of(schemaContext))).when(mountPoint)
-            .getService(DOMSchemaService.class);
-    }
-
-    protected static void mockPostBodyReader(final String identifier, final AbstractNormalizedNodeBodyReader reader) {
-        final var pathParm = new MultivaluedHashMap<String, String>(2);
-        if (!identifier.isEmpty()) {
-            pathParm.put("identifier", List.of(identifier));
-        }
-
-        final var uriInfoMock = mock(UriInfo.class);
-        doReturn(pathParm).when(uriInfoMock).getPathParameters();
-        doReturn(pathParm).when(uriInfoMock).getPathParameters(false);
-        doReturn(pathParm).when(uriInfoMock).getPathParameters(true);
-        reader.setUriInfo(uriInfoMock);
-    }
-
-    protected static void checkMountPointNormalizedNodePayload(final NormalizedNodePayload nnContext) {
-        checkNormalizedNodePayload(nnContext);
-        assertNotNull(nnContext.getInstanceIdentifierContext().getMountPoint());
-    }
-
-    protected static void checkNormalizedNodePayload(final NormalizedNodePayload nnContext) {
-        assertNotNull(nnContext.getData());
-
-        final var iid = nnContext.getInstanceIdentifierContext();
-        assertNotNull(iid);
-        assertNotNull(iid.getInstanceIdentifier());
-        assertNotNull(iid.getSchemaContext());
-        assertNotNull(iid.getSchemaNode());
-    }
-
-    protected static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
-        return mountPoint.getService(DOMSchemaService.class)
-            .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
-            .orElse(null);
-    }
-
-    protected static void assertRangeViolation(final ThrowingRunnable runnable) {
-        final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, runnable);
-        assertEquals(Status.BAD_REQUEST, ex.getResponse().getStatusInfo());
-
-        final List<RestconfError> errors = ex.getErrors();
-        assertEquals(1, errors.size());
-
-        final RestconfError error = errors.get(0);
-        assertEquals(ErrorType.APPLICATION, error.getErrorType());
-        assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
-        assertEquals("bar error app tag", error.getErrorAppTag());
-        assertEquals("bar error message", error.getErrorMessage());
-    }
-
-    protected static final List<File> loadFiles(final String resourceDirectory) throws FileNotFoundException {
-        final String path = AbstractBodyReaderTest.class.getResource(resourceDirectory).getPath();
-        final File testDir = new File(path);
-        final String[] fileList = testDir.list();
-        final List<File> testFiles = new ArrayList<>();
-        if (fileList == null) {
-            throw new FileNotFoundException(resourceDirectory);
-        }
-        for (final String fileName : fileList) {
-            if (fileName.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION)
-                && !new File(testDir, fileName).isDirectory()) {
-                testFiles.add(new File(testDir, fileName));
-            }
-        }
-        return testFiles;
-    }
-}
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/JsonBodyReaderTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/JsonBodyReaderTest.java
deleted file mode 100644 (file)
index e042199..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import java.io.FileNotFoundException;
-import javax.ws.rs.core.MediaType;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-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.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
-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.Module;
-import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
-
-public class JsonBodyReaderTest extends AbstractBodyReaderTest {
-    private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = QNameModule.create(
-        XMLNamespace.of("instance:identifier:module"), Revision.of("2014-01-17"));
-    private static final MediaType MEDIA_TYPE = new MediaType(MediaType.APPLICATION_JSON, null);
-
-    private static EffectiveModelContext schemaContext;
-
-    private final JsonNormalizedNodeBodyReader jsonBodyReader;
-
-    public JsonBodyReaderTest() {
-        super(schemaContext);
-        jsonBodyReader = new JsonNormalizedNodeBodyReader(databindProvider, mountPointService);
-    }
-
-    @BeforeClass
-    public static void initialization() throws FileNotFoundException {
-        final var testFiles = loadFiles("/instanceidentifier/yang");
-        testFiles.addAll(loadFiles("/modules"));
-        schemaContext = YangParserTestUtils.parseYangFiles(testFiles);
-    }
-
-    @Test
-    public void moduleSubContainerDataPostTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
-        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
-        final String uri = "instance-identifier-module:cont";
-        mockPostBodyReader(uri, jsonBodyReader);
-        final NormalizedNodePayload payload = jsonBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            JsonBodyReaderTest.class.getResourceAsStream("/instanceidentifier/json/json_sub_container.json"));
-        checkNormalizedNodePayload(payload);
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, payload, dataII);
-    }
-
-    @Test
-    public void moduleSubContainerDataPostActionTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-            .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
-        final QName actionQName = QName.create(dataSchemaNode.getQName(), "reset");
-        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName())
-            .node(cont1QName).node(actionQName);
-        final String uri = "instance-identifier-module:cont/cont1/reset";
-        mockPostBodyReader(uri, jsonBodyReader);
-        final NormalizedNodePayload payload = jsonBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            JsonBodyReaderTest.class.getResourceAsStream("/instanceidentifier/json/json_cont_action.json"));
-        checkNormalizedNodePayload(payload);
-        assertThat(payload.getInstanceIdentifierContext().getSchemaNode(), instanceOf(ActionDefinition.class));
-    }
-
-    @Test
-    public void moduleSubContainerAugmentDataPostTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final Module augmentModule = schemaContext.findModules(XMLNamespace.of("augment:module")).iterator().next();
-        final QName contAugmentQName = QName.create(augmentModule.getQNameModule(), "cont-augment");
-        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName(), contAugmentQName);
-        final String uri = "instance-identifier-module:cont";
-        mockPostBodyReader(uri, jsonBodyReader);
-        final NormalizedNodePayload payload = jsonBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/json/json_augment_container.json"));
-        checkNormalizedNodePayload(payload);
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, payload, dataII);
-    }
-
-    @Test
-    public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final Module augmentModule = schemaContext.findModules(XMLNamespace.of("augment:module")).iterator().next();
-        final QName augmentChoice1QName = QName.create(augmentModule.getQNameModule(), "augment-choice1");
-        final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
-        final QName containerQName = QName.create(augmentChoice1QName, "case-choice-case-container1");
-        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName())
-                .node(augmentChoice1QName).node(augmentChoice2QName).node(containerQName);
-        final String uri = "instance-identifier-module:cont";
-        mockPostBodyReader(uri, jsonBodyReader);
-        final NormalizedNodePayload payload = jsonBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/json/json_augment_choice_container.json"));
-        checkNormalizedNodePayload(payload);
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, payload, dataII);
-    }
-
-    private static void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
-            final NormalizedNodePayload nnContext, final YangInstanceIdentifier dataNodeIdent) {
-        assertEquals(dataSchemaNode, nnContext.getInstanceIdentifierContext().getSchemaNode());
-        assertEquals(dataNodeIdent, nnContext.getInstanceIdentifierContext().getInstanceIdentifier());
-        assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
-    }
-}
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderMountPointTest.java
deleted file mode 100644 (file)
index aa3b684..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import javax.ws.rs.core.MediaType;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.opendaylight.mdsal.dom.api.DOMMountPoint;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-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.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-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.NormalizedNodes;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
-
-public class XmlBodyReaderMountPointTest extends AbstractBodyReaderTest {
-    private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME =  QNameModule.create(
-        XMLNamespace.of("instance:identifier:module"), Revision.of("2014-01-17"));
-    private static final MediaType MEDIA_TYPE = new MediaType(MediaType.APPLICATION_XML, null);
-
-    private static EffectiveModelContext schemaContext;
-
-    private final XmlNormalizedNodeBodyReader xmlBodyReader;
-
-    public XmlBodyReaderMountPointTest() {
-        super(schemaContext);
-        xmlBodyReader = new XmlNormalizedNodeBodyReader(databindProvider, mountPointService);
-    }
-
-    @BeforeClass
-    public static void initialization() throws Exception {
-        final var testFiles = loadFiles("/instanceidentifier/yang");
-        testFiles.addAll(loadFiles("/invoke-rpc"));
-        schemaContext = YangParserTestUtils.parseYangFiles(testFiles);
-    }
-
-    @Test
-    public void moduleSubContainerDataPostTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont";
-        mockPostBodyReader(uri, xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderMountPointTest.class.getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml"));
-        checkMountPointNormalizedNodePayload(payload);
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, payload);
-    }
-
-    @Test
-    public void moduleSubContainerDataPostActionTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-            .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont/cont1/reset";
-        mockPostBodyReader(uri, xmlBodyReader);
-        final NormalizedNodePayload pyaload = xmlBodyReader.readFrom(null,null, null, MEDIA_TYPE, null,
-            XmlBodyReaderMountPointTest.class.getResourceAsStream("/instanceidentifier/xml/xml_cont_action.xml"));
-        checkMountPointNormalizedNodePayload(pyaload);
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, pyaload);
-    }
-
-    @Test
-    public void rpcModuleInputTest() throws Exception {
-        final String uri = "instance-identifier-module:cont/yang-ext:mount/invoke-rpc-module:rpc-test";
-        mockPostBodyReader(uri, xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderMountPointTest.class.getResourceAsStream("/invoke-rpc/xml/rpc-input.xml"));
-        checkNormalizedNodePayload(payload);
-        final ContainerNode contNode = (ContainerNode) payload.getData();
-        final ContainerNode contDataNode = (ContainerNode) contNode.getChildByArg(
-            new NodeIdentifier(QName.create(contNode.name().getNodeType(), "cont")));
-        final DataContainerChild leafDataNode = contDataNode.getChildByArg(
-            new NodeIdentifier(QName.create(contDataNode.name().getNodeType(), "lf")));
-        assertTrue("lf-test".equalsIgnoreCase(leafDataNode.body().toString()));
-    }
-
-    private static void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
-            final NormalizedNodePayload nnContext) {
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, nnContext, null);
-    }
-
-    private static void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
-            final NormalizedNodePayload nnContext, final QName qualifiedName) {
-        YangInstanceIdentifier dataNodeIdent = YangInstanceIdentifier.of(dataSchemaNode.getQName());
-        final DOMMountPoint mountPoint = nnContext.getInstanceIdentifierContext().getMountPoint();
-        final DataSchemaNode mountDataSchemaNode = modelContext(mountPoint)
-                .getDataChildByName(dataSchemaNode.getQName());
-        assertNotNull(mountDataSchemaNode);
-        if (qualifiedName != null && dataSchemaNode instanceof DataNodeContainer) {
-            final DataSchemaNode child = ((DataNodeContainer) dataSchemaNode).getDataChildByName(qualifiedName);
-            dataNodeIdent = YangInstanceIdentifier.builder(dataNodeIdent).node(child.getQName()).build();
-            assertEquals(nnContext.getInstanceIdentifierContext().getSchemaNode(), child);
-        } else {
-            assertEquals(mountDataSchemaNode, dataSchemaNode);
-        }
-        assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
-    }
-
-    /**
-     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
-     * used to distinguish between them to find correct one. Check if container was found not only according to its name
-     * but also by correct namespace used in payload.
-     */
-    @Test
-    public void findFooContainerUsingNamespaceTest() throws Exception {
-        mockPostBodyReader("instance-identifier-module:cont/yang-ext:mount", xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml"));
-
-        // check return value
-        checkMountPointNormalizedNodePayload(payload);
-        // check if container was found both according to its name and namespace
-        final var dataNodeType = payload.getData().name().getNodeType();
-        assertEquals("foo-bar-container", dataNodeType.getLocalName());
-        assertEquals("foo:module", dataNodeType.getNamespace().toString());
-    }
-
-    /**
-     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
-     * used to distinguish between them to find correct one. Check if container was found not only according to its name
-     * but also by correct namespace used in payload.
-     */
-    @Test
-    public void findBarContainerUsingNamespaceTest() throws Exception {
-        mockPostBodyReader("instance-identifier-module:cont/yang-ext:mount", xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml"));
-
-        // check return value
-        checkMountPointNormalizedNodePayload(payload);
-        // check if container was found both according to its name and namespace
-        final var dataNodeType = payload.getData().name().getNodeType();
-        assertEquals("foo-bar-container", dataNodeType.getLocalName());
-        assertEquals("bar:module", dataNodeType.getNamespace().toString());
-    }
-}
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/XmlBodyReaderTest.java
deleted file mode 100644 (file)
index b16f58a..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.nb.rfc8040.jersey.providers;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-
-import javax.ws.rs.core.MediaType;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.XMLNamespace;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-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.NormalizedNodes;
-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.Module;
-import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
-
-public class XmlBodyReaderTest extends AbstractBodyReaderTest {
-    private static final QName TOP_LEVEL_LIST = QName.create("foo", "2017-08-09", "top-level-list");
-    private static final MediaType MEDIA_TYPE = new MediaType(MediaType.APPLICATION_XML, null);
-
-    private static EffectiveModelContext schemaContext;
-
-    private final XmlNormalizedNodeBodyReader xmlBodyReader;
-
-    public XmlBodyReaderTest() {
-        super(schemaContext);
-        xmlBodyReader = new XmlNormalizedNodeBodyReader(databindProvider, mountPointService);
-    }
-
-    @BeforeClass
-    public static void initialization() throws Exception {
-        final var testFiles = loadFiles("/instanceidentifier/yang");
-        testFiles.addAll(loadFiles("/modules"));
-        testFiles.addAll(loadFiles("/foo-xml-test/yang"));
-        schemaContext = YangParserTestUtils.parseYangFiles(testFiles);
-    }
-
-    @Test
-    public void postXmlTest() throws Exception {
-        mockPostBodyReader("", xmlBodyReader);
-        runXmlTest();
-    }
-
-    private void runXmlTest() throws Exception {
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/foo-xml-test/foo.xml"));
-        assertNotNull(payload);
-
-        final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext();
-        assertEquals(YangInstanceIdentifier.of(
-            new NodeIdentifier(TOP_LEVEL_LIST),
-            NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST, QName.create(TOP_LEVEL_LIST, "key-leaf"), "key-value")),
-            iid.getInstanceIdentifier());
-
-        assertThat(payload.getData(), instanceOf(MapEntryNode.class));
-        final MapEntryNode data = (MapEntryNode) payload.getData();
-        assertEquals(2, data.size());
-        for (final DataContainerChild child : data.body()) {
-            switch (child.name().getNodeType().getLocalName()) {
-                case "key-leaf":
-                    assertEquals("key-value", child.body());
-                    break;
-                case "ordinary-leaf":
-                    assertEquals("leaf-value", child.body());
-                    break;
-                default:
-                    fail();
-            }
-        }
-    }
-
-    @Test
-    public void moduleSubContainerDataPostTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
-        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
-        final String uri = "instance-identifier-module:cont";
-        mockPostBodyReader(uri, xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml"));
-        checkNormalizedNodePayload(payload);
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, payload, dataII);
-    }
-
-    @Test
-    public void moduleSubContainerDataPostActionTest() throws Exception {
-        final var dataSchemaNode = schemaContext
-            .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
-        final QName actionQName = QName.create(dataSchemaNode.getQName(), "reset");
-        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName())
-            .node(cont1QName).node(actionQName);
-        final String uri = "instance-identifier-module:cont/cont1/reset";
-        mockPostBodyReader(uri, xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xml_cont_action.xml"));
-        checkNormalizedNodePayload(payload);
-        assertThat(payload.getInstanceIdentifierContext().getSchemaNode(), instanceOf(ActionDefinition.class));
-    }
-
-    @Test
-    public void moduleSubContainerAugmentDataPostTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final Module augmentModule = schemaContext.findModules(XMLNamespace.of("augment:module")).iterator().next();
-        final QName contAugmentQName = QName.create(augmentModule.getQNameModule(), "cont-augment");
-        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName(), contAugmentQName);
-        final String uri = "instance-identifier-module:cont";
-        mockPostBodyReader(uri, xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xml_augment_container.xml"));
-        checkNormalizedNodePayload(payload);
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, payload, dataII);
-    }
-
-    @Test
-    public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception {
-        final DataSchemaNode dataSchemaNode = schemaContext
-                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
-        final Module augmentModule = schemaContext.findModules(XMLNamespace.of("augment:module")).iterator().next();
-        final QName augmentChoice1QName = QName.create(augmentModule.getQNameModule(), "augment-choice1");
-        final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
-        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName())
-            .node(augmentChoice1QName)
-            .node(augmentChoice2QName)
-            .node(QName.create(augmentChoice1QName, "case-choice-case-container1"));
-        final String uri = "instance-identifier-module:cont";
-        mockPostBodyReader(uri, xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xml_augment_choice_container.xml"));
-        checkNormalizedNodePayload(payload);
-        checkExpectValueNormalizeNodeContext(dataSchemaNode, payload, dataII);
-    }
-
-    private static void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
-            final NormalizedNodePayload nnContext, final YangInstanceIdentifier dataNodeIdent) {
-        assertEquals(dataSchemaNode, nnContext.getInstanceIdentifierContext().getSchemaNode());
-        assertEquals(dataNodeIdent, nnContext.getInstanceIdentifierContext().getInstanceIdentifier());
-        assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
-    }
-
-    /**
-     * Test when container with the same name is placed in two modules
-     * (foo-module and bar-module). Namespace must be used to distinguish
-     * between them to find correct one. Check if container was found not only
-     * according to its name but also by correct namespace used in payload.
-     */
-    @Test
-    public void findFooContainerUsingNamespaceTest() throws Exception {
-        mockPostBodyReader("", xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml"));
-
-        // check return value
-        checkNormalizedNodePayload(payload);
-        // check if container was found both according to its name and namespace
-        final var payloadNodeType = payload.getData().name().getNodeType();
-        assertEquals("foo-bar-container", payloadNodeType.getLocalName());
-        assertEquals("foo:module", payloadNodeType.getNamespace().toString());
-    }
-
-    /**
-     * Test when container with the same name is placed in two modules
-     * (foo-module and bar-module). Namespace must be used to distinguish
-     * between them to find correct one. Check if container was found not only
-     * according to its name but also by correct namespace used in payload.
-     */
-    @Test
-    public void findBarContainerUsingNamespaceTest() throws Exception {
-        mockPostBodyReader("", xmlBodyReader);
-        final NormalizedNodePayload payload = xmlBodyReader.readFrom(null, null, null, MEDIA_TYPE, null,
-            XmlBodyReaderTest.class.getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml"));
-
-        // check return value
-        checkNormalizedNodePayload(payload);
-        // check if container was found both according to its name and namespace
-        final var payloadNodeType = payload.getData().name().getNodeType();
-        assertEquals("foo-bar-container", payloadNodeType.getLocalName());
-        assertEquals("bar:module", payloadNodeType.getNamespace().toString());
-    }
-}
index b1050e8586919055f3ab500de6a5603b64f9e0b9..452614e4c5943368afcfdb413710b0ce8a683ff8 100644 (file)
@@ -7,77 +7,60 @@
  */
 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;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 
 import com.google.common.util.concurrent.Futures;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
-import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.nb.rfc8040.AbstractInstanceIdentifierTest;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.Uint32;
-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.DataSchemaContextTree;
-import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class Netconf799Test extends AbstractInstanceIdentifierTest {
-    private static final QName CONT_QNAME = QName.create("instance:identifier:module", "2014-01-17", "cont");
-    private static final QName CONT1_QNAME = QName.create(CONT_QNAME, "cont1");
-    private static final QName RESET_QNAME = QName.create(CONT_QNAME, "reset");
-
-    private static final QName DELAY_QNAME = QName.create(CONT_QNAME, "delay");
-    private static final QName INPUT_QNAME = QName.create(CONT_QNAME, "input");
     private static final QName OUTPUT_QNAME = QName.create(CONT_QNAME, "output");
-    private static final YangInstanceIdentifier ACTION_YII = YangInstanceIdentifier.of(CONT_QNAME).node(CONT1_QNAME);
+
+    @Mock
+    private DOMDataBroker dataBroker;
+    @Mock
+    private DOMActionService actionService;
+    @Mock
+    private DOMMountPointService mountPointService;
+    @Mock
+    private RestconfStreamsSubscriptionService restconfStreamSubService;
 
     @Test
     public void testInvokeAction() {
-        final DOMDataBroker mockDataBroker = mock(DOMDataBroker.class);
-
-        final DOMActionService actionService = mock(DOMActionService.class);
         doReturn(Futures.immediateFuture(new SimpleDOMActionResult(
             Builders.containerBuilder().withNodeIdentifier(NodeIdentifier.create(OUTPUT_QNAME)).build())))
             .when(actionService).invokeAction(eq(Absolute.of(CONT_QNAME, CONT1_QNAME, RESET_QNAME)), any(), any());
 
-        final RestconfDataServiceImpl dataService = new RestconfDataServiceImpl(
-            () -> DatabindContext.ofModel(IID_SCHEMA), mockDataBroker, mock(DOMMountPointService.class),
-            mock(RestconfStreamsSubscriptionService.class), actionService, new StreamsConfiguration(0, 1, 0, false));
-
-        final var nodeAndStack = DataSchemaContextTree.from(IID_SCHEMA).enterPath(ACTION_YII).orElseThrow();
-        final var node = nodeAndStack.node().dataSchemaNode();
-        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(
-            InstanceIdentifierContext.ofPath(stack, actionNode, ACTION_YII, null),
-            Builders.containerBuilder()
-                .withNodeIdentifier(NodeIdentifier.create(INPUT_QNAME))
-                .withChild(ImmutableNodes.leafNode(DELAY_QNAME, Uint32.TEN))
-                .build()));
+        final var dataService = new RestconfDataServiceImpl(
+            () -> DatabindContext.ofModel(IID_SCHEMA), dataBroker, mountPointService, restconfStreamSubService,
+            actionService, new StreamsConfiguration(0, 1, 0, false));
 
+        final var response = dataService.postDataJSON("instance-identifier-module:cont/cont1/reset",
+            stringInputStream("""
+            {
+              "instance-identifier-module:input": {
+                "delay": 600
+              }
+            }"""), null);
         assertEquals(204, response.getStatus());
         assertNull(response.getEntity());
     }
index 952082c3f6f41a51225cee0c01ed000c086e5658..01e0e2080819fa4e8798aaffb9025341f86ebf41 100644 (file)
@@ -330,15 +330,16 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
     @Test
     public void testPostData() {
         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
-        final var node = JUKEBOX_IID.node(BAND_ENTRY.name());
-        doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
-        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, BAND_ENTRY);
+        doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
+        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID,
+            Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME)).build());
         doReturn(UriBuilder.fromUri("http://localhost:8181/rests/")).when(uriInfo).getBaseUriBuilder();
 
-        final var response = dataService.postData(NormalizedNodePayload.of(
-            InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, JUKEBOX_IID),
-            Builders.mapBuilder().withNodeIdentifier(PLAYLIST_NID).withChild(BAND_ENTRY).build()),
-            uriInfo);
+        final var response = dataService.postDataJSON(new ByteArrayInputStream("""
+            {
+              "example-jukebox:jukebox" : {
+              }
+            }""".getBytes(StandardCharsets.UTF_8)), uriInfo);
         assertEquals(201, response.getStatus());
         assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox"), response.getLocation());
     }
@@ -351,9 +352,13 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, BAND_ENTRY);
         doReturn(UriBuilder.fromUri("http://localhost:8181/rests/")).when(uriInfo).getBaseUriBuilder();
 
-        final var response = dataService.postData(NormalizedNodePayload.of(
-            InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, PLAYLIST_IID),
-            Builders.mapBuilder().withNodeIdentifier(PLAYLIST_NID).withChild(BAND_ENTRY).build()),
+        final var response = dataService.postDataJSON("example-jukebox:jukebox", new ByteArrayInputStream("""
+            {
+              "example-jukebox:playlist" : {
+                "name" : "name of band",
+                "description" : "band description"
+              }
+            }""".getBytes(StandardCharsets.UTF_8)),
             uriInfo);
         assertEquals(201, response.getStatus());
         assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox/playlist=name%20of%20band"),
diff --git a/restconf/restconf-nb/src/test/resources/instanceidentifier/json/json_cont_action.json b/restconf/restconf-nb/src/test/resources/instanceidentifier/json/json_cont_action.json
deleted file mode 100644 (file)
index 82da66d..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "instance-identifier-module:input": {
-    "delay": 600
-  }
-}
\ No newline at end of file
diff --git a/restconf/restconf-nb/src/test/resources/instanceidentifier/xml/xml_cont_action.xml b/restconf/restconf-nb/src/test/resources/instanceidentifier/xml/xml_cont_action.xml
deleted file mode 100644 (file)
index 9f39ed4..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<input xmlns="instance:identifier:module">
-  <delay>600</delay>
-</input>
\ No newline at end of file