Use ServerError in PatchStatus{Context,Entity} 21/111721/1
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 18 May 2024 20:11:31 +0000 (22:11 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 18 May 2024 20:27:46 +0000 (22:27 +0200)
RestconfError is not properly structured, use ServerError, which
correctly models errors. This in turn allows PatchStatusContext to not
have a DatabindContext.

JIRA: NETCONF-1188
Change-Id: I288ad55de771474930362eb570ba315f5b700905
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/jaxrs/JaxRsRestconf.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/server/api/PatchStatusContext.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/PatchStatusEntity.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/spi/YangPatchStatusBody.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategyTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategyTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/server/spi/YangPatchStatusBodyTest.java

index 52e8bcd406706372b50db3e4650eebe3f9518f59..e24dc222f92729e7e09f89172e4ee8069383f2cc 100644 (file)
@@ -51,7 +51,6 @@ import org.opendaylight.restconf.api.MediaTypes;
 import org.opendaylight.restconf.api.QueryParameters;
 import org.opendaylight.restconf.api.query.PrettyPrintParam;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfFuture;
 import org.opendaylight.restconf.nb.rfc8040.ErrorTagMapping;
 import org.opendaylight.restconf.nb.rfc8040.URLConstants;
@@ -72,6 +71,7 @@ import org.opendaylight.restconf.server.api.ModulesGetResult;
 import org.opendaylight.restconf.server.api.OperationInputBody;
 import org.opendaylight.restconf.server.api.PatchStatusContext;
 import org.opendaylight.restconf.server.api.RestconfServer;
+import org.opendaylight.restconf.server.api.ServerError;
 import org.opendaylight.restconf.server.api.ServerRequest;
 import org.opendaylight.restconf.server.api.XmlChildBody;
 import org.opendaylight.restconf.server.api.XmlDataPostBody;
@@ -455,8 +455,8 @@ public final class JaxRsRestconf implements ParamConverterProvider {
                 return HttpStatusCode.INTERNAL_SERVER_ERROR;
             }
 
-            private @NonNull HttpStatusCode statusOfFirst(final List<RestconfError> error) {
-                return errorTagMapping.statusOf(error.get(0).getErrorTag());
+            private @NonNull HttpStatusCode statusOfFirst(final List<ServerError> error) {
+                return errorTagMapping.statusOf(error.get(0).tag());
             }
         });
     }
index 752847b9c600596640581a0e13dd56bb8f012c37..472e666c690fc17298203dfe3c11ab4e0a54c1bf 100644 (file)
@@ -49,6 +49,7 @@ import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.api.ApiPath;
+import org.opendaylight.restconf.api.ErrorMessage;
 import org.opendaylight.restconf.api.FormattableBody;
 import org.opendaylight.restconf.api.query.ContentParam;
 import org.opendaylight.restconf.api.query.WithDefaultsParam;
@@ -83,6 +84,9 @@ import org.opendaylight.restconf.server.api.PatchBody;
 import org.opendaylight.restconf.server.api.PatchStatusContext;
 import org.opendaylight.restconf.server.api.PatchStatusEntity;
 import org.opendaylight.restconf.server.api.ResourceBody;
+import org.opendaylight.restconf.server.api.ServerError;
+import org.opendaylight.restconf.server.api.ServerErrorInfo;
+import org.opendaylight.restconf.server.api.ServerErrorPath;
 import org.opendaylight.restconf.server.api.ServerRequest;
 import org.opendaylight.restconf.server.spi.ApiPathCanonizer;
 import org.opendaylight.restconf.server.spi.ApiPathNormalizer;
@@ -633,7 +637,7 @@ public abstract class RestconfStrategy implements DatabindAware {
                             tx.create(targetNode, patchEntity.getNode());
                             editCollection.add(new PatchStatusEntity(editId, true, null));
                         } catch (RestconfDocumentedException e) {
-                            editCollection.add(new PatchStatusEntity(editId, false, e.getErrors()));
+                            editCollection.add(new PatchStatusEntity(editId, false, convertErrors(e.getErrors())));
                             noError = false;
                         }
                         break;
@@ -642,7 +646,7 @@ public abstract class RestconfStrategy implements DatabindAware {
                             tx.delete(targetNode);
                             editCollection.add(new PatchStatusEntity(editId, true, null));
                         } catch (RestconfDocumentedException e) {
-                            editCollection.add(new PatchStatusEntity(editId, false, e.getErrors()));
+                            editCollection.add(new PatchStatusEntity(editId, false, convertErrors(e.getErrors())));
                             noError = false;
                         }
                         break;
@@ -652,7 +656,7 @@ public abstract class RestconfStrategy implements DatabindAware {
                             tx.merge(targetNode, patchEntity.getNode());
                             editCollection.add(new PatchStatusEntity(editId, true, null));
                         } catch (RestconfDocumentedException e) {
-                            editCollection.add(new PatchStatusEntity(editId, false, e.getErrors()));
+                            editCollection.add(new PatchStatusEntity(editId, false, convertErrors(e.getErrors())));
                             noError = false;
                         }
                         break;
@@ -661,7 +665,7 @@ public abstract class RestconfStrategy implements DatabindAware {
                             tx.replace(targetNode, patchEntity.getNode());
                             editCollection.add(new PatchStatusEntity(editId, true, null));
                         } catch (RestconfDocumentedException e) {
-                            editCollection.add(new PatchStatusEntity(editId, false, e.getErrors()));
+                            editCollection.add(new PatchStatusEntity(editId, false, convertErrors(e.getErrors())));
                             noError = false;
                         }
                         break;
@@ -670,13 +674,13 @@ public abstract class RestconfStrategy implements DatabindAware {
                             tx.remove(targetNode);
                             editCollection.add(new PatchStatusEntity(editId, true, null));
                         } catch (RestconfDocumentedException e) {
-                            editCollection.add(new PatchStatusEntity(editId, false, e.getErrors()));
+                            editCollection.add(new PatchStatusEntity(editId, false, convertErrors(e.getErrors())));
                             noError = false;
                         }
                         break;
                     default:
                         editCollection.add(new PatchStatusEntity(editId, false, List.of(
-                            new RestconfError(ErrorType.PROTOCOL, ErrorTag.OPERATION_NOT_SUPPORTED,
+                            new ServerError(ErrorType.PROTOCOL, ErrorTag.OPERATION_NOT_SUPPORTED,
                                 "Not supported Yang Patch operation"))));
                         noError = false;
                         break;
@@ -691,7 +695,7 @@ public abstract class RestconfStrategy implements DatabindAware {
         if (!noError) {
             tx.cancel();
             ret.set(new DataYangPatchResult(
-                new PatchStatusContext(databind(), patch.patchId(), List.copyOf(editCollection), false, null)));
+                new PatchStatusContext(patch.patchId(), List.copyOf(editCollection), false, null)));
             return ret;
         }
 
@@ -699,21 +703,38 @@ public abstract class RestconfStrategy implements DatabindAware {
             @Override
             public void onSuccess(final CommitInfo result) {
                 ret.set(new DataYangPatchResult(
-                    new PatchStatusContext(databind(), patch.patchId(), List.copyOf(editCollection), true, null)));
+                    new PatchStatusContext(patch.patchId(), List.copyOf(editCollection), true, null)));
             }
 
             @Override
             public void onFailure(final Throwable cause) {
                 // if errors occurred during transaction commit then patch failed and global errors are reported
                 ret.set(new DataYangPatchResult(
-                    new PatchStatusContext(databind(), patch.patchId(), List.copyOf(editCollection), false,
-                        TransactionUtil.decodeException(cause, "PATCH", null, modelContext()).getErrors())));
+                    new PatchStatusContext(patch.patchId(), List.copyOf(editCollection), false, convertErrors(
+                        TransactionUtil.decodeException(cause, "PATCH", null, modelContext()).getErrors()))));
             }
         }, MoreExecutors.directExecutor());
 
         return ret;
     }
 
+    @Deprecated
+    private ServerError convertError(final RestconfError error) {
+        final var message = error.getErrorMessage();
+        final var path = error.getErrorPath();
+        final var info = error.getErrorInfo();
+
+        return new ServerError(error.getErrorType(), error.getErrorTag(),
+            message != null ? new ErrorMessage(message) : null, error.getErrorAppTag(),
+            path != null ? new ServerErrorPath(databind, path) : null,
+            info != null ? new ServerErrorInfo(info) : null);
+    }
+
+    @Deprecated
+    private List<ServerError> convertErrors(final List<RestconfError> errors) {
+        return errors.stream().map(this::convertError).collect(Collectors.toUnmodifiableList());
+    }
+
     private static void insertWithPointPost(final RestconfTransaction tx, final YangInstanceIdentifier path,
             final NormalizedNode data, final PathArgument pointArg, final NormalizedNodeContainer<?> readList,
             final boolean before) {
index d0ffa487520eb3197861ea5c5d4a978c3f3da648..7a31ee267a9d3281fc1069bba8c5f94f04681d2b 100644 (file)
@@ -12,20 +12,17 @@ import static java.util.Objects.requireNonNull;
 import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.restconf.common.errors.RestconfError;
 
 /**
  * Holder of patch status context.
  */
 public record PatchStatusContext(
-    @NonNull DatabindContext databind,
     @NonNull String patchId,
     @NonNull List<PatchStatusEntity> editCollection,
     boolean ok,
-    @Nullable List<RestconfError> globalErrors) implements DatabindAware {
+    @Nullable List<ServerError> globalErrors) {
 
     public PatchStatusContext {
-        requireNonNull(databind);
         requireNonNull(patchId);
         requireNonNull(editCollection);
     }
index 70234ea7d410dd52a0827194b4dda879446013ed..e6039104e6b4de17bc1e466235ec48b10506473c 100644 (file)
@@ -5,20 +5,19 @@
  * 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.server.api;
 
+import static java.util.Objects.requireNonNull;
+
 import java.util.List;
-import org.opendaylight.restconf.common.errors.RestconfError;
 
 public class PatchStatusEntity {
-
     private final String editId;
-    private final List<RestconfError> editErrors;
+    private final List<ServerError> editErrors;
     private final boolean ok;
 
-    public PatchStatusEntity(final String editId, final boolean ok, final List<RestconfError> editErrors) {
-        this.editId = editId;
+    public PatchStatusEntity(final String editId, final boolean ok, final List<ServerError> editErrors) {
+        this.editId = requireNonNull(editId);
         this.ok = ok;
         this.editErrors = editErrors;
     }
@@ -31,7 +30,7 @@ public class PatchStatusEntity {
         return ok;
     }
 
-    public List<RestconfError> getEditErrors() {
+    public List<ServerError> getEditErrors() {
         return editErrors;
     }
 }
index b7a9ae79526e9f8ca40c1fc15c1e95861c4ad81c..b9b65c8e7855bd354309c5641cf7c04acc1d32a5 100644 (file)
@@ -19,8 +19,8 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import org.opendaylight.restconf.api.FormattableBody;
 import org.opendaylight.restconf.api.query.PrettyPrintParam;
-import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.server.api.PatchStatusContext;
+import org.opendaylight.restconf.server.api.ServerError;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.patch.rev170222.yang.patch.status.YangPatchStatus;
 import org.opendaylight.yangtools.yang.data.codec.gson.DefaultJSONValueWriter;
 
@@ -120,27 +120,27 @@ public final class YangPatchStatusBody extends FormattableBody {
         writer.name("ok").beginArray().nullValue().endArray();
     }
 
-    private void writeErrors(final List<RestconfError> errors, final JsonWriter writer) throws IOException {
+    private static void writeErrors(final List<ServerError> errors, final JsonWriter writer) throws IOException {
         writer.name("errors").beginObject().name("error").beginArray();
 
-        for (var restconfError : errors) {
+        for (var error : errors) {
             writer.beginObject()
-                .name("error-type").value(restconfError.getErrorType().elementBody())
-                .name("error-tag").value(restconfError.getErrorTag().elementBody());
+                .name("error-type").value(error.type().elementBody())
+                .name("error-tag").value(error.tag().elementBody());
 
-            final var errorPath = restconfError.getErrorPath();
+            final var errorPath = error.path();
             if (errorPath != null) {
                 writer.name("error-path");
-                status.databind().jsonCodecs().instanceIdentifierCodec()
-                    .writeValue(new DefaultJSONValueWriter(writer), errorPath);
+                errorPath.databind().jsonCodecs().instanceIdentifierCodec()
+                    .writeValue(new DefaultJSONValueWriter(writer), errorPath.path());
             }
-            final var errorMessage = restconfError.getErrorMessage();
+            final var errorMessage = error.message();
             if (errorMessage != null) {
-                writer.name("error-message").value(errorMessage);
+                writer.name("error-message").value(errorMessage.elementBody());
             }
-            final var errorInfo = restconfError.getErrorInfo();
+            final var errorInfo = error.info();
             if (errorInfo != null) {
-                writer.name("error-info").value(errorInfo);
+                writer.name("error-info").value(errorInfo.elementBody());
             }
 
             writer.endObject();
@@ -149,40 +149,40 @@ public final class YangPatchStatusBody extends FormattableBody {
         writer.endArray().endObject();
     }
 
-    private void reportErrors(final List<RestconfError> errors, final XMLStreamWriter writer)
+    private static void reportErrors(final List<ServerError> errors, final XMLStreamWriter writer)
             throws XMLStreamException {
         writer.writeStartElement("errors");
 
         for (var restconfError : errors) {
             writer.writeStartElement("error-type");
-            writer.writeCharacters(restconfError.getErrorType().elementBody());
+            writer.writeCharacters(restconfError.type().elementBody());
             writer.writeEndElement();
 
             writer.writeStartElement("error-tag");
-            writer.writeCharacters(restconfError.getErrorTag().elementBody());
+            writer.writeCharacters(restconfError.tag().elementBody());
             writer.writeEndElement();
 
             // optional node
-            final var errorPath = restconfError.getErrorPath();
+            final var errorPath = restconfError.path();
             if (errorPath != null) {
                 writer.writeStartElement("error-path");
-                status.databind().xmlCodecs().instanceIdentifierCodec().writeValue(writer, errorPath);
+                errorPath.databind().xmlCodecs().instanceIdentifierCodec().writeValue(writer, errorPath.path());
                 writer.writeEndElement();
             }
 
             // optional node
-            final var errorMessage = restconfError.getErrorMessage();
+            final var errorMessage = restconfError.message();
             if (errorMessage != null) {
                 writer.writeStartElement("error-message");
-                writer.writeCharacters(errorMessage);
+                writer.writeCharacters(errorMessage.elementBody());
                 writer.writeEndElement();
             }
 
             // optional node
-            final var errorInfo = restconfError.getErrorInfo();
+            final var errorInfo = restconfError.info();
             if (errorInfo != null) {
                 writer.writeStartElement("error-info");
-                writer.writeCharacters(errorInfo);
+                writer.writeCharacters(errorInfo.elementBody());
                 writer.writeEndElement();
             }
         }
index 64c0af3957ace201b90f553b20d26c4049b43707..5fc1b353abe1ac68b2b9aace59468bdded69e1e3 100644 (file)
@@ -42,6 +42,7 @@ import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.api.ApiPath;
+import org.opendaylight.restconf.api.ErrorMessage;
 import org.opendaylight.restconf.api.query.ContentParam;
 import org.opendaylight.restconf.api.query.WithDefaultsParam;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
@@ -230,9 +231,9 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         final var editErrors = edit.getEditErrors();
         assertEquals(1, editErrors.size());
         final var editError = editErrors.get(0);
-        assertEquals("Data does not exist", editError.getErrorMessage());
-        assertEquals(ErrorType.PROTOCOL, editError.getErrorType());
-        assertEquals(ErrorTag.DATA_MISSING, editError.getErrorTag());
+        assertEquals(new ErrorMessage("Data does not exist"), editError.message());
+        assertEquals(ErrorType.PROTOCOL, editError.type());
+        assertEquals(ErrorTag.DATA_MISSING, editError.tag());
     }
 
     @Override
index 1d2a8e624f3576b1292aeb602b270e4697910a8d..a25cb63e862852040513692c954cc66f0ca510f1 100644 (file)
@@ -35,6 +35,7 @@ import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.api.ApiPath;
+import org.opendaylight.restconf.api.ErrorMessage;
 import org.opendaylight.restconf.api.QueryParameters;
 import org.opendaylight.restconf.api.query.InsertParam;
 import org.opendaylight.restconf.api.query.PointParam;
@@ -370,9 +371,9 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         assertNotNull(globalErrors);
         assertEquals(1, globalErrors.size());
         final var globalError = globalErrors.get(0);
-        assertEquals("Data does not exist", globalError.getErrorMessage());
-        assertEquals(ErrorType.PROTOCOL, globalError.getErrorType());
-        assertEquals(ErrorTag.DATA_MISSING, globalError.getErrorTag());
+        assertEquals(new ErrorMessage("Data does not exist"), globalError.message());
+        assertEquals(ErrorType.PROTOCOL, globalError.type());
+        assertEquals(ErrorTag.DATA_MISSING, globalError.tag());
     }
 
     @Override
index c88577b1030636335ccd047194c292d003da6ca1..61ece25aaa848ef728bc319914f7e88d8c23b60a 100644 (file)
@@ -7,33 +7,28 @@
  */
 package org.opendaylight.restconf.server.spi;
 
-import static org.mockito.Mockito.mock;
-
 import java.io.IOException;
 import java.util.List;
 import org.junit.Test;
-import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.nb.rfc8040.AbstractJukeboxTest;
-import org.opendaylight.restconf.server.api.DatabindContext;
 import org.opendaylight.restconf.server.api.PatchStatusContext;
 import org.opendaylight.restconf.server.api.PatchStatusEntity;
+import org.opendaylight.restconf.server.api.ServerError;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 public class YangPatchStatusBodyTest extends AbstractJukeboxTest {
-    private final RestconfError error = new RestconfError(ErrorType.PROTOCOL, new ErrorTag("data-exists"),
+    private final ServerError error = new ServerError(ErrorType.PROTOCOL, new ErrorTag("data-exists"),
         "Data already exists");
     private final PatchStatusEntity statusEntity = new PatchStatusEntity("patch1", true, null);
     private final PatchStatusEntity statusEntityError = new PatchStatusEntity("patch1", false, List.of(error));
-    private final DatabindContext databind = DatabindContext.ofModel(mock(EffectiveModelContext.class));
 
     /**
      * Test if per-operation status is omitted if global error is present.
      */
     @Test
     public void testOutputWithGlobalError() throws IOException {
-        final var body = new YangPatchStatusBody(new PatchStatusContext(databind, "patch", List.of(statusEntity), false,
+        final var body = new YangPatchStatusBody(new PatchStatusContext("patch", List.of(statusEntity), false,
             List.of(error)));
 
         assertFormat("""
@@ -67,8 +62,8 @@ public class YangPatchStatusBodyTest extends AbstractJukeboxTest {
      */
     @Test
     public void testOutputWithoutGlobalError() throws IOException {
-        final var body = new YangPatchStatusBody(new PatchStatusContext(databind,"patch", List.of(statusEntityError),
-            false, null));
+        final var body = new YangPatchStatusBody(new PatchStatusContext("patch", List.of(statusEntityError), false,
+            null));
 
         assertFormat("""
             {