Eliminate NormalizedNodePayload
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / server / spi / YangPatchStatusBody.java
1 /*
2  * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.server.spi;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects.ToStringHelper;
13 import com.google.gson.stream.JsonWriter;
14 import java.io.IOException;
15 import java.io.OutputStream;
16 import java.util.List;
17 import javax.xml.XMLConstants;
18 import javax.xml.stream.XMLStreamException;
19 import javax.xml.stream.XMLStreamWriter;
20 import org.opendaylight.restconf.api.FormattableBody;
21 import org.opendaylight.restconf.api.query.PrettyPrintParam;
22 import org.opendaylight.restconf.common.errors.RestconfError;
23 import org.opendaylight.restconf.server.api.PatchStatusContext;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.patch.rev170222.yang.patch.status.YangPatchStatus;
25
26 /**
27  * Result of a {@code PATCH} request as defined in
28  * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2.3">RFC8072, section 2.3</a>.
29  */
30 public final class YangPatchStatusBody extends FormattableBody {
31     private static final String IETF_YANG_PATCH_NAMESPACE = YangPatchStatus.QNAME.getNamespace().toString();
32
33     private final PatchStatusContext status;
34
35     public YangPatchStatusBody(final PatchStatusContext status) {
36         this.status = requireNonNull(status);
37     }
38
39     @Override
40     public void formatToJSON(final PrettyPrintParam prettyPrint, final OutputStream out) throws IOException {
41         try (var writer = FormattableBodySupport.createJsonWriter(out, prettyPrint)) {
42             writer.beginObject().name("ietf-yang-patch:yang-patch-status")
43                 .beginObject().name("patch-id").value(status.patchId());
44
45             if (status.ok()) {
46                 writeOk(writer);
47             } else {
48                 final var globalErrors = status.globalErrors();
49                 if (globalErrors != null) {
50                     writeErrors(globalErrors, writer);
51                 } else {
52                     writer.name("edit-status").beginObject()
53                         .name("edit").beginArray();
54                     for (var editStatus : status.editCollection()) {
55                         writer.beginObject().name("edit-id").value(editStatus.getEditId());
56
57                         final var editErrors = editStatus.getEditErrors();
58                         if (editErrors != null) {
59                             writeErrors(editErrors, writer);
60                         } else if (editStatus.isOk()) {
61                             writeOk(writer);
62                         }
63                         writer.endObject();
64                     }
65                     writer.endArray().endObject();
66                 }
67             }
68             writer.endObject().endObject();
69         }
70     }
71
72     @Override
73     public void formatToXML(final PrettyPrintParam prettyPrint, final OutputStream out) throws IOException {
74         final var writer = FormattableBodySupport.createXmlWriter(out, prettyPrint);
75         try {
76             formatToXML(writer);
77         } catch (XMLStreamException e) {
78             throw new IOException("Failed to write body", e);
79         }
80     }
81
82     private void formatToXML(final XMLStreamWriter writer) throws XMLStreamException {
83         writer.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "yang-patch-status", IETF_YANG_PATCH_NAMESPACE);
84         writer.writeDefaultNamespace(IETF_YANG_PATCH_NAMESPACE);
85         writer.writeStartElement("patch-id");
86         writer.writeCharacters(status.patchId());
87         writer.writeEndElement();
88
89         if (status.ok()) {
90             writer.writeEmptyElement("ok");
91         } else {
92             final var globalErrors = status.globalErrors();
93             if (globalErrors != null) {
94                 reportErrors(globalErrors, writer);
95             } else {
96                 writer.writeStartElement("edit-status");
97                 for (var patchStatusEntity : status.editCollection()) {
98                     writer.writeStartElement("edit");
99                     writer.writeStartElement("edit-id");
100                     writer.writeCharacters(patchStatusEntity.getEditId());
101                     writer.writeEndElement();
102
103                     final var editErrors = patchStatusEntity.getEditErrors();
104                     if (editErrors != null) {
105                         reportErrors(editErrors, writer);
106                     } else if (patchStatusEntity.isOk()) {
107                         writer.writeEmptyElement("ok");
108                     }
109                     writer.writeEndElement();
110                 }
111                 writer.writeEndElement();
112             }
113         }
114         writer.writeEndElement();
115         writer.close();
116     }
117
118     private static void writeOk(final JsonWriter writer) throws IOException {
119         writer.name("ok").beginArray().nullValue().endArray();
120     }
121
122     private void writeErrors(final List<RestconfError> errors, final JsonWriter writer) throws IOException {
123         writer.name("errors").beginObject().name("error").beginArray();
124
125         for (var restconfError : errors) {
126             writer.beginObject()
127                 .name("error-type").value(restconfError.getErrorType().elementBody())
128                 .name("error-tag").value(restconfError.getErrorTag().elementBody());
129
130             final var errorPath = restconfError.getErrorPath();
131             if (errorPath != null) {
132                 writer.name("error-path");
133                 status.databind().jsonCodecs().instanceIdentifierCodec().writeValue(writer, errorPath);
134             }
135             final var errorMessage = restconfError.getErrorMessage();
136             if (errorMessage != null) {
137                 writer.name("error-message").value(errorMessage);
138             }
139             final var errorInfo = restconfError.getErrorInfo();
140             if (errorInfo != null) {
141                 writer.name("error-info").value(errorInfo);
142             }
143
144             writer.endObject();
145         }
146
147         writer.endArray().endObject();
148     }
149
150     private void reportErrors(final List<RestconfError> errors, final XMLStreamWriter writer)
151             throws XMLStreamException {
152         writer.writeStartElement("errors");
153
154         for (var restconfError : errors) {
155             writer.writeStartElement("error-type");
156             writer.writeCharacters(restconfError.getErrorType().elementBody());
157             writer.writeEndElement();
158
159             writer.writeStartElement("error-tag");
160             writer.writeCharacters(restconfError.getErrorTag().elementBody());
161             writer.writeEndElement();
162
163             // optional node
164             final var errorPath = restconfError.getErrorPath();
165             if (errorPath != null) {
166                 writer.writeStartElement("error-path");
167                 status.databind().xmlCodecs().instanceIdentifierCodec().writeValue(writer, errorPath);
168                 writer.writeEndElement();
169             }
170
171             // optional node
172             final var errorMessage = restconfError.getErrorMessage();
173             if (errorMessage != null) {
174                 writer.writeStartElement("error-message");
175                 writer.writeCharacters(errorMessage);
176                 writer.writeEndElement();
177             }
178
179             // optional node
180             final var errorInfo = restconfError.getErrorInfo();
181             if (errorInfo != null) {
182                 writer.writeStartElement("error-info");
183                 writer.writeCharacters(errorInfo);
184                 writer.writeEndElement();
185             }
186         }
187
188         writer.writeEndElement();
189     }
190
191     @Override
192     protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
193         return helper.add("status", status);
194     }
195 }