*/
package org.opendaylight.restconf.nb.rfc8040.databind;
-import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.io.InputStream;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.server.api.DataPutPath;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev170126.restconf.restconf.Data;
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.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
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.impl.schema.ImmutableNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* @param path A {@link YangInstanceIdentifier} corresponding to the body
* @throws RestconfDocumentedException if the body cannot be decoded or it does not match {@code path}
*/
- // TODO: pass down DatabindContext corresponding to inference
@SuppressWarnings("checkstyle:illegalCatch")
- public @NonNull NormalizedNode toNormalizedNode(final @NonNull DataPutPath path,
- final @NonNull SchemaNode schemaNode) {
+ public @NonNull NormalizedNode toNormalizedNode(final @NonNull DataPutPath path) {
final var instance = path.instance();
final var expectedName = instance.isEmpty() ? DATA_NID : instance.getLastPathArgument();
final var holder = new NormalizationResultHolder();
ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
- validateListKeysEqualityInPayloadAndUri(schemaNode, instance, data);
return data;
}
abstract void streamTo(@NonNull DataPutPath path, @NonNull PathArgument name, @NonNull InputStream inputStream,
@NonNull NormalizedNodeStreamWriter writer) throws IOException;
-
- /**
- * Validates whether keys in {@code payload} are equal to values of keys in
- * {@code iiWithData} for list schema node.
- *
- * @throws RestconfDocumentedException if key values or key count in payload and URI isn't equal
- */
- @VisibleForTesting
- static final void validateListKeysEqualityInPayloadAndUri(final SchemaNode schemaNode,
- final YangInstanceIdentifier path, final NormalizedNode data) {
- if (schemaNode instanceof ListSchemaNode listSchema
- && path.getLastPathArgument() instanceof NodeIdentifierWithPredicates nip
- && data instanceof MapEntryNode mapEntry) {
- isEqualUriAndPayloadKeyValues(nip.asMap(), mapEntry, listSchema.getKeyDefinition());
- }
- }
-
- private static void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final MapEntryNode payload,
- final List<QName> keyDefinitions) {
- final var mutableCopyUriKeyValues = new HashMap<>(uriKeyValues);
- for (var keyDefinition : keyDefinitions) {
- final var uriKeyValue = mutableCopyUriKeyValues.remove(keyDefinition);
- if (uriKeyValue == null) {
- throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.",
- ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
- }
-
- final var dataKeyValue = payload.name().getValue(keyDefinition);
- if (!uriKeyValue.equals(dataKeyValue)) {
- throw new RestconfDocumentedException("The value '" + uriKeyValue
- + "' for key '" + keyDefinition.getLocalName()
- + "' specified in the URI doesn't match the value '" + dataKeyValue
- + "' specified in the message body. ", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
- }
- }
- }
}
\ No newline at end of file
final ResourceBody body) {
final var putPath = new DataPutPath(reqPath.databind(), reqPath.inference(), reqPath.getInstanceIdentifier());
return new ResourceRequest(getRestconfStrategy(putPath.databind(), reqPath.getMountPoint()), putPath.instance(),
- body.toNormalizedNode(putPath, reqPath.getSchemaNode()));
+ body.toNormalizedNode(putPath));
}
@VisibleForTesting
try (var body = bodyConstructor.apply(stringInputStream(patchBody))) {
final var context = InstanceIdentifierContext.ofApiPath(apiPath, DATABIND, mountPointService);
return body.toNormalizedNode(
- new DataPutPath(context.databind(), context.inference(), context.getInstanceIdentifier()),
- context.getSchemaNode());
+ new DataPutPath(context.databind(), context.inference(), context.getInstanceIdentifier()));
}
}
package org.opendaylight.restconf.nb.rfc8040.databind;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Map;
import org.junit.jupiter.api.Test;
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.YangInstanceIdentifier.NodeIdentifierWithPredicates;
}
@Test
- void testRangeViolation() throws Exception {
+ void testRangeViolation() {
assertRangeViolation(() -> parse("netconf786:foo", """
{
"netconf786:foo": {
}
@Test
- void testMismatchedInput() throws Exception {
+ void testMismatchedInput() {
final var error = assertError(() -> parse("base:cont", """
{
"ietf-restconf:restconf-state" : {
assertEquals(ErrorType.PROTOCOL, error.getErrorType());
assertEquals(ErrorTag.MALFORMED_MESSAGE, error.getErrorTag());
}
+
+ @Test
+ void testMissingKeys() {
+ final var ex = assertThrows(IllegalArgumentException.class,
+ () -> parse("nested-module:depth1-cont/depth2-list2=one,two", """
+ {
+ "depth2-list2" : {
+ "depth3-lf1-key" : "one"
+ }
+ }"""));
+ assertNull(ex.getMessage());
+ }
+
+ @Test
+ void testJukeboxBand() throws Exception {
+ final var one = QName.create("urn:nested:module", "2014-06-03", "depth3-lf1-key");
+ final var two = QName.create("urn:nested:module", "2014-06-03", "depth3-lf2-key");
+
+ assertEquals(Builders.mapEntryBuilder()
+ .withNodeIdentifier(NodeIdentifierWithPredicates.of(
+ QName.create("urn:nested:module", "2014-06-03", "depth2-list2"), Map.of(one, "one", two, "two")))
+ .withChild(ImmutableNodes.leafNode(one, "one"))
+ .withChild(ImmutableNodes.leafNode(two, "two"))
+ .build(), parse("nested-module:depth1-cont/depth2-list2=one,two", """
+ {
+ "depth2-list2" : {
+ "depth3-lf1-key" : "one",
+ "depth3-lf2-key" : "two"
+ }
+ }"""));
+ }
}
+++ /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 org.junit.Test;
-import org.opendaylight.restconf.nb.rfc8040.AbstractJukeboxTest;
-import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-
-public class ResourceBodyTest extends AbstractJukeboxTest {
- @Test
- public void testValidateListKeysEqualityInPayloadAndUri() {
- final var path = YangInstanceIdentifier.builder()
- .node(JUKEBOX_QNAME)
- .node(PLAYLIST_QNAME)
- .nodeWithKey(PLAYLIST_QNAME, NAME_QNAME, "name of band")
- .build();
- final var iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_DATABIND, path);
- ResourceBody.validateListKeysEqualityInPayloadAndUri(iidContext.getSchemaNode(), path, BAND_ENTRY);
- }
-}
package org.opendaylight.restconf.nb.rfc8040.databind;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
assertEquals(ErrorTag.MALFORMED_MESSAGE, error.getErrorTag());
}
+ @Test
+ void testMissingKeys() throws Exception {
+ final var ex = assertThrows(IllegalArgumentException.class,
+ () -> parse("nested-module:depth1-cont/depth2-list2=one,two", """
+ <depth2-list2 xmlns="urn:nested:module">
+ <depth3-lf1-key>one</depth3-lf1-key>
+ </depth2-list2>"""));
+ assertNull(ex.getMessage());
+ }
}