Remove RestconfDocumentedException.status
[netconf.git] / restconf / restconf-common / src / main / java / org / opendaylight / restconf / common / errors / RestconfDocumentedException.java
index 6bc02fe73372fe1e210b38cfbd6b80d10e8d4d0d..c04d9d98e8b5ef4a3df9c7d53351622f1c98ff21 100644 (file)
@@ -9,38 +9,36 @@ package org.opendaylight.restconf.common.errors;
 
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Response.Status;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
-import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.ErrorTags;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.OperationFailedException;
 import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.YangError;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.codec.YangInvalidValueException;
+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
- * client.
- *
- * <p>
- * See also <a href="https://tools.ietf.org/html/draft-bierman-netconf-restconf-02">RESTCONF</a>
+ * Unchecked exception to communicate error information, as defined
+ * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-3.9">"errors" YANG Data Template</a>.
  *
  * @author Devin Avery
  * @author Thomas Pantelis
  */
-public class RestconfDocumentedException extends WebApplicationException {
+public class RestconfDocumentedException extends RuntimeException {
+    @java.io.Serial
+    private static final long serialVersionUID = 3L;
 
-    private static final long serialVersionUID = 1L;
+    private final List<RestconfError> errors;
 
-    private final ImmutableList<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
@@ -50,7 +48,7 @@ public class RestconfDocumentedException extends WebApplicationException {
      *            A string which provides a plain text string describing the error.
      */
     public RestconfDocumentedException(final String message) {
-        this(message, RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED);
+        this(message, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
     }
 
     /**
@@ -67,8 +65,7 @@ public class RestconfDocumentedException extends WebApplicationException {
      */
     public RestconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
                                        final Throwable cause) {
-        this(cause, new RestconfError(errorType, errorTag, message, null,
-                cause.getMessage(), null));
+        this(cause, new RestconfError(errorType, errorTag, message, null, cause.getMessage(), null));
     }
 
     /**
@@ -112,8 +109,8 @@ public class RestconfDocumentedException extends WebApplicationException {
      *            The underlying exception cause.
      */
     public RestconfDocumentedException(final String message, final Throwable cause) {
-        this(cause, new RestconfError(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED,
-                message, null, cause.getMessage(), null));
+        this(cause, new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, message, null,
+            cause.getMessage(), null));
     }
 
     /**
@@ -130,14 +127,13 @@ public class RestconfDocumentedException extends WebApplicationException {
         // FIXME: We override getMessage so supplied message is lost for any public access
         // this was lost also in original code.
         super(cause);
-        if (!errors.isEmpty()) {
-            this.errors = ImmutableList.copyOf(errors);
+        if (errors.isEmpty()) {
+            this.errors = List.of(new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, message));
         } else {
-            this.errors = ImmutableList.of(new RestconfError(RestconfError.ErrorType.APPLICATION,
-                    RestconfError.ErrorTag.OPERATION_FAILED, message));
+            this.errors = List.copyOf(errors);
         }
 
-        status = null;
+        modelContext = null;
     }
 
     /**
@@ -148,30 +144,34 @@ public class RestconfDocumentedException extends WebApplicationException {
         this(message, cause, convertToRestconfErrors(rpcErrors));
     }
 
-    /**
-     * Constructs an instance with an HTTP status and no error information.
-     *
-     * @param status
-     *            the HTTP status.
-     */
-    public RestconfDocumentedException(final Status status) {
-        errors = ImmutableList.of();
-        this.status = requireNonNull(status, "Status can't be null");
+    public RestconfDocumentedException(final Throwable cause, final RestconfError error) {
+        super(cause);
+        errors = List.of(error);
+        modelContext = null;
     }
 
-    public RestconfDocumentedException(final Throwable cause, final RestconfError error) {
-        super(cause, error.getErrorTag().getStatusCode());
-        errors = ImmutableList.of(error);
-        status = null;
+    public RestconfDocumentedException(final Throwable cause, final RestconfError error,
+            final EffectiveModelContext modelContext) {
+        super(cause);
+        errors = List.of(error);
+        this.modelContext = requireNonNull(modelContext);
+    }
+
+    public RestconfDocumentedException(final Throwable cause, final List<RestconfError> errors) {
+        super(cause);
+        if (errors.isEmpty()) {
+            throw new IllegalArgumentException("At least one error is required");
+        }
+        this.errors = List.copyOf(errors);
+        modelContext = null;
     }
 
     public static RestconfDocumentedException decodeAndThrow(final String message,
             final OperationFailedException cause) {
         for (final RpcError error : cause.getErrorList()) {
-            if (error.getErrorType() == RpcError.ErrorType.TRANSPORT
-                    && error.getTag().equals(ErrorTag.RESOURCE_DENIED.getTagValue())) {
+            if (error.getErrorType() == ErrorType.TRANSPORT && error.getTag().equals(ErrorTag.RESOURCE_DENIED)) {
                 throw new RestconfDocumentedException(error.getMessage(), ErrorType.TRANSPORT,
-                    ErrorTag.RESOURCE_DENIED_TRANSPORT, cause);
+                    ErrorTags.RESOURCE_DENIED_TRANSPORT, cause);
             }
         }
         throw new RestconfDocumentedException(message, cause, cause.getErrorList());
@@ -232,41 +232,48 @@ public class RestconfDocumentedException extends WebApplicationException {
     }
 
     /**
-     * Throw an instance of this exception if the specified exception has a {@link YangError} attachment.
+     * Throw an instance of this exception if the specified exception has a {@link YangNetconfError} attachment.
      *
      * @param cause Proposed cause of a RestconfDocumented exception
      */
     public static void throwIfYangError(final Throwable cause) {
-        if (cause instanceof YangError) {
-            final YangError error = (YangError) cause;
-            throw new RestconfDocumentedException(cause, new RestconfError(ErrorType.valueOf(error.getErrorType()),
-                // FIXME: this is a special-case until we have YangError.getTag()
-                cause instanceof YangInvalidValueException ? ErrorTag.INVALID_VALUE : ErrorTag.MALFORMED_MESSAGE,
-                    error.getErrorMessage().orElse(null), error.getErrorAppTag().orElse(null)));
+        if (cause instanceof YangNetconfErrorAware infoAware) {
+            throw new RestconfDocumentedException(cause, infoAware.getNetconfErrors().stream()
+                .map(error -> new RestconfError(error.type(), error.tag(), error.message(), error.appTag(),
+                    // FIXME: pass down error info
+                    null, error.path()))
+                .toList());
         }
     }
 
     private static List<RestconfError> convertToRestconfErrors(final Collection<? extends RpcError> rpcErrors) {
-        final List<RestconfError> errorList = new ArrayList<>();
-        if (rpcErrors != null) {
-            for (RpcError rpcError : rpcErrors) {
-                errorList.add(new RestconfError(rpcError));
-            }
+        if (rpcErrors == null || rpcErrors.isEmpty()) {
+            return List.of();
         }
 
+        final var errorList = new ArrayList<RestconfError>();
+        for (var rpcError : rpcErrors) {
+            errorList.add(new RestconfError(rpcError));
+        }
         return errorList;
     }
 
-    public List<RestconfError> getErrors() {
-        return errors;
+    @Override
+    public String getMessage() {
+        return "errors: " + errors;
     }
 
-    public Status getStatus() {
-        return status;
+    /**
+     * 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;
     }
 
-    @Override
-    public String getMessage() {
-        return "errors: " + errors + (status != null ? ", status: " + status : "");
+    public List<RestconfError> getErrors() {
+        return errors;
     }
 }