Add option to get device context in ExceptionMapper 74/107874/13
authorPeter Suna <peter.suna@pantheon.tech>
Mon, 11 Sep 2023 13:54:21 +0000 (15:54 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 18 Oct 2023 16:57:59 +0000 (18:57 +0200)
To correctly emit device error-path, it is required
to have the device context in the mapper provided by
RestconfDocumentedException.

JIRA: NETCONF-1130
Change-Id: I0050391f349485e5909359e2f23df46111d92546
Signed-off-by: Peter Suna <peter.suna@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/errors/RestconfDocumentedException.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategy.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/TransactionUtil.java

index 54f8d3e2406f15938502271f1627dcb015a58f6b..3613ecf1d74226cbe71f28551f2bd6430a1badbe 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.restconf.common.errors;
 
 import static java.util.Objects.requireNonNull;
 
-import java.io.Serial;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -25,6 +24,7 @@ import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangNetconfError;
 import org.opendaylight.yangtools.yang.data.api.YangNetconfErrorAware;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 /**
  * Unchecked exception to communicate error information, as defined in the ietf restcong draft, to be sent to the
@@ -37,12 +37,15 @@ import org.opendaylight.yangtools.yang.data.api.YangNetconfErrorAware;
  * @author Thomas Pantelis
  */
 public class RestconfDocumentedException extends WebApplicationException {
-    @Serial
+    @java.io.Serial
     private static final long serialVersionUID = 2L;
 
     private final List<RestconfError> errors;
     private final Status status;
 
+    // FIXME: this field should be non-null
+    private final transient @Nullable EffectiveModelContext modelContext;
+
     /**
      * Constructs an instance with an error message. The error type defaults to APPLICATION and the error tag defaults
      * to OPERATION_FAILED.
@@ -137,6 +140,7 @@ public class RestconfDocumentedException extends WebApplicationException {
         }
 
         status = null;
+        modelContext = null;
     }
 
     /**
@@ -155,6 +159,7 @@ public class RestconfDocumentedException extends WebApplicationException {
      */
     public RestconfDocumentedException(final Status status) {
         errors = List.of();
+        modelContext = null;
         this.status = requireNonNull(status, "Status can't be null");
     }
 
@@ -162,12 +167,22 @@ public class RestconfDocumentedException extends WebApplicationException {
         super(cause, ErrorTags.statusOf(error.getErrorTag()));
         errors = List.of(error);
         status = null;
+        modelContext = null;
     }
 
     public RestconfDocumentedException(final Throwable cause, final List<RestconfError> errors) {
         super(cause, ErrorTags.statusOf(errors.get(0).getErrorTag()));
         this.errors = List.copyOf(errors);
         status = null;
+        modelContext = null;
+    }
+
+    public RestconfDocumentedException(final Throwable cause, final RestconfError error,
+            final EffectiveModelContext modelContext) {
+        super(cause, ErrorTags.statusOf(error.getErrorTag()));
+        errors = List.of(error);
+        status = null;
+        this.modelContext = requireNonNull(modelContext);
     }
 
     public static RestconfDocumentedException decodeAndThrow(final String message,
@@ -262,6 +277,16 @@ public class RestconfDocumentedException extends WebApplicationException {
         return errorList;
     }
 
+    /**
+     * Reference to {@link EffectiveModelContext} in which this exception was generated. This method will return
+     * {@code null} if this exception was serialized or if the context is not available.
+     *
+     * @return Reference model context
+     */
+    public @Nullable EffectiveModelContext modelContext() {
+        return modelContext;
+    }
+
     public List<RestconfError> getErrors() {
         return errors;
     }
index a3f567ed200ae4a9fa824fef636dd354c6f8a4f3..9f3c3c963cce7601f68caf46b1d3f00b382d6b87 100644 (file)
@@ -54,7 +54,7 @@ public final class NetconfRestconfStrategy extends RestconfStrategy {
 
             @Override
             public void onFailure(final Throwable cause) {
-                future.setFailure(TransactionUtil.decodeException(cause, "DELETE", path));
+                future.setFailure(TransactionUtil.decodeException(cause, "DELETE", path, modelContext()));
             }
         }, MoreExecutors.directExecutor());
     }
index 18f21c5339a47841645aa0cccd04f67187d35415..e9c5da5fd6741c6d90e0527c88d2aabbe066e3cb 100644 (file)
@@ -228,7 +228,7 @@ public abstract class RestconfStrategy {
 
             @Override
             public void onFailure(final Throwable cause) {
-                future.setFailure(TransactionUtil.decodeException(cause, "MERGE", path));
+                future.setFailure(TransactionUtil.decodeException(cause, "MERGE", path, modelContext));
             }
         }, MoreExecutors.directExecutor());
     }
@@ -264,7 +264,7 @@ public abstract class RestconfStrategy {
 
             @Override
             public void onFailure(final Throwable cause) {
-                ret.setFailure(TransactionUtil.decodeException(cause, "PUT", path));
+                ret.setFailure(TransactionUtil.decodeException(cause, "PUT", path, modelContext));
             }
         }, MoreExecutors.directExecutor());
 
@@ -391,7 +391,7 @@ public abstract class RestconfStrategy {
 
             @Override
             public void onFailure(final Throwable cause) {
-                ret.setFailure(TransactionUtil.decodeException(cause, "POST", path));
+                ret.setFailure(TransactionUtil.decodeException(cause, "POST", path, modelContext));
             }
         }, MoreExecutors.directExecutor());
 
@@ -533,7 +533,7 @@ public abstract class RestconfStrategy {
             public void onFailure(final Throwable cause) {
                 // if errors occurred during transaction commit then patch failed and global errors are reported
                 ret.set(new PatchStatusContext(modelContext, patch.patchId(), List.copyOf(editCollection), false,
-                    TransactionUtil.decodeException(cause, "PATCH", null).getErrors()));
+                    TransactionUtil.decodeException(cause, "PATCH", null, modelContext).getErrors()));
             }
         }, MoreExecutors.directExecutor());
 
index 09c1190e489ed44d8247213f820521198d263a17..bfc2c5f30c30e1f4511101880b3777e1408def7c 100644 (file)
@@ -19,6 +19,7 @@ import org.opendaylight.restconf.common.errors.RestconfError;
 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.model.api.EffectiveModelContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,7 +54,7 @@ final class TransactionUtil {
     }
 
     static @NonNull RestconfDocumentedException decodeException(final Throwable ex, final String txType,
-            final YangInstanceIdentifier path) {
+            final YangInstanceIdentifier path, final EffectiveModelContext context) {
         if (ex instanceof TransactionCommitFailedException) {
             // If device send some error message we want this message to get to client and not just to throw it away
             // or override it with new generic message. We search for NetconfDocumentedException that was send from
@@ -64,11 +65,11 @@ final class TransactionUtil {
                     if (errorTag.equals(ErrorTag.DATA_EXISTS)) {
                         LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
                         return new RestconfDocumentedException(ex, new RestconfError(ErrorType.PROTOCOL,
-                            ErrorTag.DATA_EXISTS, "Data already exists", path));
+                            ErrorTag.DATA_EXISTS, "Data already exists", path), context);
                     } else if (errorTag.equals(ErrorTag.DATA_MISSING)) {
                         LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
                         return new RestconfDocumentedException(ex, new RestconfError(ErrorType.PROTOCOL,
-                            ErrorTag.DATA_MISSING, "Data does not exist", path));
+                            ErrorTag.DATA_MISSING, "Data does not exist", path), context);
                     }
                 } else if (error instanceof NetconfDocumentedException netconfError) {
                     return new RestconfDocumentedException(netconfError.getMessage(), netconfError.getErrorType(),