Fix parsing of union values 86/110986/5
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 21 Mar 2024 14:11:17 +0000 (15:11 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 21 Mar 2024 14:33:40 +0000 (15:33 +0100)
TypeDefinitionAwareCodec is no longer handling unions because it cannot
reliably do so -- because they may indirect through leafref, or end up
being a complex type.

Since we are already handling leafref, identityref and
instance-identifier correctly, complete the translation by adding
explicit handling for unions.

JIRA: NETCONF-1265
Change-Id: I88980ba18df8e52d3395d5f0bcf8b8ef0c187949
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/spi/ApiPathNormalizer.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/server/spi/NC1265Test.java

index 58f8e4982d7567a04272a3e27841847e841177ee..6bd98ff075cf21c11b0a2c438fa664880069a39b 100644 (file)
@@ -49,6 +49,7 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStateme
 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 
@@ -361,6 +362,9 @@ public final class ApiPathNormalizer implements PointNormalizer {
         if (typedef instanceof InstanceIdentifierTypeDefinition) {
             return toInstanceIdentifier(value, schemaNode);
         }
+        if (typedef instanceof UnionTypeDefinition union) {
+            return toUnion(stack, schemaNode, union, value);
+        }
 
         // Simple types
         final var codec = verifyNotNull(TypeDefinitionAwareCodec.from(typedef), "Unhandled type %s decoding %s",
@@ -381,6 +385,27 @@ public final class ApiPathNormalizer implements PointNormalizer {
             keyValues.get(0)));
     }
 
+    private Object toUnion(final SchemaInferenceStack stack, final TypedDataSchemaNode schemaNode,
+            final UnionTypeDefinition union, final @NonNull String value) {
+        // As per https://www.rfc-editor.org/rfc/rfc7950#section-9.12:
+        //   'type union' must have at least one 'type'
+        // hence this variable will always end up being non-null before being used
+        RestconfDocumentedException cause = null;
+        for (var type : union.getTypes()) {
+            try {
+                return prepareValueByType(stack, schemaNode, type, value);
+            } catch (RestconfDocumentedException e) {
+                if (cause == null) {
+                    cause = e;
+                } else {
+                    cause.addSuppressed(e);
+                }
+            }
+        }
+        throw new RestconfDocumentedException("Invalid value '" + value + "' for " + schemaNode.getQName(),
+            ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, cause);
+    }
+
     private YangInstanceIdentifier toInstanceIdentifier(final String value, final TypedDataSchemaNode schemaNode) {
         if (value.isEmpty() || !value.startsWith("/")) {
             throw new RestconfDocumentedException("Invalid value '" + value + "' for " + schemaNode.getQName(),
index 2d7808be8ebb1ee561fe5864625e753fcfe7b776..b745117bbb4cb968f7e613a90c159a26b7e7bc23 100644 (file)
@@ -11,7 +11,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.text.ParseException;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.opendaylight.restconf.api.ApiPath;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
@@ -61,7 +60,6 @@ class NC1265Test {
     }
 
     @Test
-    @Disabled("relies on TypeDefinitionAwareCodec's union handling")
     void unionKeyInstanceIdentifier() {
         assertNormalized(YangInstanceIdentifier.builder()
             .node(XYZZY)
@@ -72,6 +70,30 @@ class NC1265Test {
             .build(), "nc1265:xyzzy=%2Fnc1265:baz=123");
     }
 
+    @Test
+    void unionKeyIdentityref() {
+        assertNormalized(YangInstanceIdentifier.builder()
+            .node(XYZZY)
+            .nodeWithKey(XYZZY, KEY, QName.create("nc1265", "base-id"))
+            .build(), "nc1265:xyzzy=nc1265:base-id");
+    }
+
+    @Test
+    void unionKeyLeafref() {
+        assertNormalized(YangInstanceIdentifier.builder()
+            .node(XYZZY)
+            .nodeWithKey(XYZZY, KEY, Uint8.valueOf(123))
+            .build(), "nc1265:xyzzy=123");
+    }
+
+    @Test
+    void unionKeyString() {
+        assertNormalized(YangInstanceIdentifier.builder()
+            .node(XYZZY)
+            .nodeWithKey(XYZZY, KEY, "abc")
+            .build(), "nc1265:xyzzy=abc");
+    }
+
     @Test
     void noslashInstanceIdentifierKey() {
         final var error = assertRestconfError("nc1265:bar=nc1265:baz=123");