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;
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 {
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 {
public @Nullable YangInstanceIdentifier getInstanceIdentifier() {
return null;
}
-
- @Override
- InstanceIdentifierContext createWithConcapt(final List<PathArgument> concatArgs) {
- return this;
- }
}
private final @NonNull SchemaNode schemaNode;
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;
}
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;
*/
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);
}
@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();
}
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;
@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));
}
}
.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())
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),
/**
* 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;
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
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;
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;
/**
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);
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");
}
}
}
--- /dev/null
+/*
+ * 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);
+ }
+}
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;
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));
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
-
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;
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;
};
}
+ /**
+ * 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}
*/
@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();
* @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();
}
/**
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");
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+}
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"));
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
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());
+ }
}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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()));
+ }
+}
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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));
- }
-}
+++ /dev/null
-/*
- * 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());
- }
-}
+++ /dev/null
-/*
- * 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());
- }
-}
*/
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());
}
@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());
}
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"),
+++ /dev/null
-{
- "instance-identifier-module:input": {
- "delay": 600
- }
-}
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<input xmlns="instance:identifier:module">
- <delay>600</delay>
-</input>
\ No newline at end of file