2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.restconf.nb.jaxrs;
10 import static java.util.Objects.requireNonNull;
12 import java.io.InputStream;
13 import java.time.Clock;
14 import java.time.LocalDateTime;
15 import java.time.format.DateTimeFormatter;
16 import java.util.List;
17 import java.util.function.Function;
18 import javax.ws.rs.Consumes;
19 import javax.ws.rs.DELETE;
20 import javax.ws.rs.Encoded;
21 import javax.ws.rs.GET;
22 import javax.ws.rs.PATCH;
23 import javax.ws.rs.POST;
24 import javax.ws.rs.PUT;
25 import javax.ws.rs.Path;
26 import javax.ws.rs.PathParam;
27 import javax.ws.rs.Produces;
28 import javax.ws.rs.container.AsyncResponse;
29 import javax.ws.rs.container.Suspended;
30 import javax.ws.rs.core.Context;
31 import javax.ws.rs.core.MediaType;
32 import javax.ws.rs.core.Response;
33 import javax.ws.rs.core.Response.Status;
34 import javax.ws.rs.core.UriInfo;
35 import org.opendaylight.restconf.api.MediaTypes;
36 import org.opendaylight.restconf.common.errors.RestconfError;
37 import org.opendaylight.restconf.common.errors.RestconfFuture;
38 import org.opendaylight.restconf.common.patch.PatchStatusContext;
39 import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
40 import org.opendaylight.restconf.nb.rfc8040.databind.JsonChildBody;
41 import org.opendaylight.restconf.nb.rfc8040.databind.JsonDataPostBody;
42 import org.opendaylight.restconf.nb.rfc8040.databind.JsonOperationInputBody;
43 import org.opendaylight.restconf.nb.rfc8040.databind.JsonPatchBody;
44 import org.opendaylight.restconf.nb.rfc8040.databind.JsonResourceBody;
45 import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
46 import org.opendaylight.restconf.nb.rfc8040.databind.XmlChildBody;
47 import org.opendaylight.restconf.nb.rfc8040.databind.XmlDataPostBody;
48 import org.opendaylight.restconf.nb.rfc8040.databind.XmlOperationInputBody;
49 import org.opendaylight.restconf.nb.rfc8040.databind.XmlPatchBody;
50 import org.opendaylight.restconf.nb.rfc8040.databind.XmlResourceBody;
51 import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
52 import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
53 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
54 import org.opendaylight.restconf.server.api.DataPostResult;
55 import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
56 import org.opendaylight.restconf.server.api.DataPostResult.InvokeOperation;
57 import org.opendaylight.restconf.server.api.DataPutResult;
58 import org.opendaylight.restconf.server.api.OperationsGetResult;
59 import org.opendaylight.restconf.server.api.RestconfServer;
60 import org.opendaylight.restconf.server.spi.OperationOutput;
61 import org.opendaylight.yangtools.yang.common.Empty;
62 import org.opendaylight.yangtools.yang.common.Revision;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
67 * Baseline RESTCONF implementation with JAX-RS. Interfaces to a {@link RestconfServer}.
70 public final class JaxRsRestconf {
71 private static final Logger LOG = LoggerFactory.getLogger(JaxRsRestconf.class);
72 private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
74 private final RestconfServer server;
76 public JaxRsRestconf(final RestconfServer server) {
77 this.server = requireNonNull(server);
81 * Delete the target data resource.
83 * @param identifier path to target
84 * @param ar {@link AsyncResponse} which needs to be completed
87 @Path("/data/{identifier:.+}")
88 @SuppressWarnings("checkstyle:abbreviationAsWordInName")
89 public void dataDELETE(@Encoded @PathParam("identifier") final String identifier,
90 @Suspended final AsyncResponse ar) {
91 server.dataDELETE(identifier).addCallback(new JaxRsRestconfCallback<>(ar) {
93 Response transform(final Empty result) {
94 return Response.noContent().build();
100 * Get target data resource from data root.
102 * @param uriInfo URI info
103 * @param ar {@link AsyncResponse} which needs to be completed
108 MediaTypes.APPLICATION_YANG_DATA_JSON,
109 MediaTypes.APPLICATION_YANG_DATA_XML,
110 MediaType.APPLICATION_JSON,
111 MediaType.APPLICATION_XML,
114 public void dataGET(@Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
115 final var readParams = QueryParams.newReadDataParams(uriInfo);
116 completeDataGET(server.dataGET(readParams), readParams, ar);
120 * Get target data resource.
122 * @param identifier path to target
123 * @param uriInfo URI info
124 * @param ar {@link AsyncResponse} which needs to be completed
127 @Path("/data/{identifier:.+}")
129 MediaTypes.APPLICATION_YANG_DATA_JSON,
130 MediaTypes.APPLICATION_YANG_DATA_XML,
131 MediaType.APPLICATION_JSON,
132 MediaType.APPLICATION_XML,
135 public void dataGET(@Encoded @PathParam("identifier") final String identifier,
136 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
137 final var readParams = QueryParams.newReadDataParams(uriInfo);
138 completeDataGET(server.dataGET(identifier, readParams), readParams, ar);
141 private static void completeDataGET(final RestconfFuture<NormalizedNodePayload> future,
142 final ReadDataParams readParams, final AsyncResponse ar) {
143 future.addCallback(new JaxRsRestconfCallback<>(ar) {
145 Response transform(final NormalizedNodePayload result) {
146 return switch (readParams.content()) {
147 case ALL, CONFIG -> {
148 final var type = result.data().name().getNodeType();
149 yield Response.status(Status.OK)
151 // FIXME: is this ETag okay?
152 .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null)
153 + "-" + type.getLocalName() + '"')
154 .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC())))
157 case NONCONFIG -> Response.status(Status.OK).entity(result).build();
164 * Partially modify the target data store, as defined in
165 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
167 * @param body data node for put to config DS
168 * @param ar {@link AsyncResponse} which needs to be completed
173 MediaTypes.APPLICATION_YANG_DATA_XML,
174 MediaType.APPLICATION_XML,
177 public void dataXmlPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
178 try (var xmlBody = new XmlResourceBody(body)) {
179 completeDataPATCH(server.dataPATCH(xmlBody), ar);
184 * Partially modify the target data resource, as defined in
185 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
187 * @param identifier path to target
188 * @param body data node for put to config DS
189 * @param ar {@link AsyncResponse} which needs to be completed
192 @Path("/data/{identifier:.+}")
194 MediaTypes.APPLICATION_YANG_DATA_XML,
195 MediaType.APPLICATION_XML,
198 public void dataXmlPATCH(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
199 @Suspended final AsyncResponse ar) {
200 try (var xmlBody = new XmlResourceBody(body)) {
201 completeDataPATCH(server.dataPATCH(identifier, xmlBody), ar);
206 * Partially modify the target data store, as defined in
207 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
209 * @param body data node for put to config DS
210 * @param ar {@link AsyncResponse} which needs to be completed
215 MediaTypes.APPLICATION_YANG_DATA_JSON,
216 MediaType.APPLICATION_JSON,
218 public void dataJsonPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
219 try (var jsonBody = new JsonResourceBody(body)) {
220 completeDataPATCH(server.dataPATCH(jsonBody), ar);
225 * Partially modify the target data resource, as defined in
226 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
228 * @param identifier path to target
229 * @param body data node for put to config DS
230 * @param ar {@link AsyncResponse} which needs to be completed
233 @Path("/data/{identifier:.+}")
235 MediaTypes.APPLICATION_YANG_DATA_JSON,
236 MediaType.APPLICATION_JSON,
238 public void dataJsonPATCH(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
239 @Suspended final AsyncResponse ar) {
240 try (var jsonBody = new JsonResourceBody(body)) {
241 completeDataPATCH(server.dataPATCH(identifier, jsonBody), ar);
245 private static void completeDataPATCH(final RestconfFuture<Empty> future, final AsyncResponse ar) {
246 future.addCallback(new JaxRsRestconfCallback<>(ar) {
248 Response transform(final Empty result) {
249 return Response.ok().build();
255 * Ordered list of edits that are applied to the datastore by the server, as defined in
256 * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2">RFC8072, section 2</a>.
258 * @param body YANG Patch body
259 * @param ar {@link AsyncResponse} which needs to be completed with a {@link PatchStatusContext}
263 @Consumes(MediaTypes.APPLICATION_YANG_PATCH_JSON)
265 MediaTypes.APPLICATION_YANG_DATA_JSON,
266 MediaTypes.APPLICATION_YANG_DATA_XML
268 public void dataYangJsonPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
269 try (var jsonBody = new JsonPatchBody(body)) {
270 completeDataYangPATCH(server.dataPATCH(jsonBody), ar);
275 * Ordered list of edits that are applied to the target datastore by the server, as defined in
276 * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2">RFC8072, section 2</a>.
278 * @param identifier path to target
279 * @param body YANG Patch body
280 * @param ar {@link AsyncResponse} which needs to be completed with a {@link PatchStatusContext}
283 @Path("/data/{identifier:.+}")
284 @Consumes(MediaTypes.APPLICATION_YANG_PATCH_JSON)
286 MediaTypes.APPLICATION_YANG_DATA_JSON,
287 MediaTypes.APPLICATION_YANG_DATA_XML
289 public void dataYangJsonPATCH(@Encoded @PathParam("identifier") final String identifier,
290 final InputStream body, @Suspended final AsyncResponse ar) {
291 try (var jsonBody = new JsonPatchBody(body)) {
292 completeDataYangPATCH(server.dataPATCH(identifier, jsonBody), ar);
297 * Ordered list of edits that are applied to the datastore by the server, as defined in
298 * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2">RFC8072, section 2</a>.
300 * @param body YANG Patch body
301 * @param ar {@link AsyncResponse} which needs to be completed with a {@link PatchStatusContext}
305 @Consumes(MediaTypes.APPLICATION_YANG_PATCH_XML)
307 MediaTypes.APPLICATION_YANG_DATA_JSON,
308 MediaTypes.APPLICATION_YANG_DATA_XML
310 public void dataYangXmlPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
311 try (var xmlBody = new XmlPatchBody(body)) {
312 completeDataYangPATCH(server.dataPATCH(xmlBody), ar);
317 * Ordered list of edits that are applied to the target datastore by the server, as defined in
318 * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2">RFC8072, section 2</a>.
320 * @param identifier path to target
321 * @param body YANG Patch body
322 * @param ar {@link AsyncResponse} which needs to be completed with a {@link PatchStatusContext}
325 @Path("/data/{identifier:.+}")
326 @Consumes(MediaTypes.APPLICATION_YANG_PATCH_XML)
328 MediaTypes.APPLICATION_YANG_DATA_JSON,
329 MediaTypes.APPLICATION_YANG_DATA_XML
331 public void dataYangXmlPATCH(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
332 @Suspended final AsyncResponse ar) {
333 try (var xmlBody = new XmlPatchBody(body)) {
334 completeDataYangPATCH(server.dataPATCH(identifier, xmlBody), ar);
338 private static void completeDataYangPATCH(final RestconfFuture<PatchStatusContext> future, final AsyncResponse ar) {
339 future.addCallback(new JaxRsRestconfCallback<>(ar) {
341 Response transform(final PatchStatusContext result) {
342 return Response.status(statusOf(result)).entity(result).build();
345 private static Status statusOf(final PatchStatusContext result) {
349 final var globalErrors = result.globalErrors();
350 if (globalErrors != null && !globalErrors.isEmpty()) {
351 return statusOfFirst(globalErrors);
353 for (var edit : result.editCollection()) {
355 final var editErrors = edit.getEditErrors();
356 if (editErrors != null && !editErrors.isEmpty()) {
357 return statusOfFirst(editErrors);
361 return Status.INTERNAL_SERVER_ERROR;
364 private static Status statusOfFirst(final List<RestconfError> error) {
365 return ErrorTags.statusOf(error.get(0).getErrorTag());
371 * Create a top-level data resource.
373 * @param body data node for put to config DS
374 * @param uriInfo URI info
375 * @param ar {@link AsyncResponse} which needs to be completed
380 MediaTypes.APPLICATION_YANG_DATA_JSON,
381 MediaType.APPLICATION_JSON,
383 public void postDataJSON(final InputStream body, @Context final UriInfo uriInfo,
384 @Suspended final AsyncResponse ar) {
385 try (var jsonBody = new JsonChildBody(body)) {
386 completeDataPOST(server.dataPOST(jsonBody, QueryParams.normalize(uriInfo)), uriInfo, ar);
391 * Create a data resource in target.
393 * @param identifier path to target
394 * @param body data node for put to config DS
395 * @param uriInfo URI info
396 * @param ar {@link AsyncResponse} which needs to be completed
399 @Path("/data/{identifier:.+}")
401 MediaTypes.APPLICATION_YANG_DATA_JSON,
402 MediaType.APPLICATION_JSON,
404 public void postDataJSON(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
405 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
406 completeDataPOST(server.dataPOST(identifier, new JsonDataPostBody(body), QueryParams.normalize(uriInfo)),
411 * Create a top-level data resource.
413 * @param body data node for put to config DS
414 * @param uriInfo URI info
415 * @param ar {@link AsyncResponse} which needs to be completed
420 MediaTypes.APPLICATION_YANG_DATA_XML,
421 MediaType.APPLICATION_XML,
424 public void postDataXML(final InputStream body, @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
425 try (var xmlBody = new XmlChildBody(body)) {
426 completeDataPOST(server.dataPOST(xmlBody, QueryParams.normalize(uriInfo)), uriInfo, ar);
431 * Create a data resource in target.
433 * @param identifier path to target
434 * @param body data node for put to config DS
435 * @param uriInfo URI info
436 * @param ar {@link AsyncResponse} which needs to be completed
439 @Path("/data/{identifier:.+}")
441 MediaTypes.APPLICATION_YANG_DATA_XML,
442 MediaType.APPLICATION_XML,
445 public void postDataXML(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
446 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
447 completeDataPOST(server.dataPOST(identifier, new XmlDataPostBody(body), QueryParams.normalize(uriInfo)),
451 private static void completeDataPOST(final RestconfFuture<? extends DataPostResult> future, final UriInfo uriInfo,
452 final AsyncResponse ar) {
453 future.addCallback(new JaxRsRestconfCallback<DataPostResult>(ar) {
455 Response transform(final DataPostResult result) {
456 if (result instanceof CreateResource createResource) {
457 return Response.created(uriInfo.getBaseUriBuilder()
459 .path(createResource.createdPath())
463 if (result instanceof InvokeOperation invokeOperation) {
464 final var output = invokeOperation.output();
465 return output == null ? Response.status(Status.NO_CONTENT).build()
466 : Response.status(Status.OK).entity(output).build();
468 LOG.error("Unhandled result {}", result);
469 return Response.serverError().build();
475 * Replace the data store.
477 * @param uriInfo request URI information
478 * @param body data node for put to config DS
479 * @param ar {@link AsyncResponse} which needs to be completed
484 MediaTypes.APPLICATION_YANG_DATA_JSON,
485 MediaType.APPLICATION_JSON,
487 public void dataJsonPUT(@Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
488 try (var jsonBody = new JsonResourceBody(body)) {
489 completeDataPUT(server.dataPUT(jsonBody, QueryParams.normalize(uriInfo)), ar);
494 * Create or replace the target data resource.
496 * @param identifier path to target
497 * @param uriInfo request URI information
498 * @param body data node for put to config DS
499 * @param ar {@link AsyncResponse} which needs to be completed
502 @Path("/data/{identifier:.+}")
504 MediaTypes.APPLICATION_YANG_DATA_JSON,
505 MediaType.APPLICATION_JSON,
507 public void dataJsonPUT(@Encoded @PathParam("identifier") final String identifier,
508 @Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
509 try (var jsonBody = new JsonResourceBody(body)) {
510 completeDataPUT(server.dataPUT(identifier, jsonBody, QueryParams.normalize(uriInfo)), ar);
515 * Replace the data store.
517 * @param uriInfo request URI information
518 * @param body data node for put to config DS
519 * @param ar {@link AsyncResponse} which needs to be completed
524 MediaTypes.APPLICATION_YANG_DATA_XML,
525 MediaType.APPLICATION_XML,
528 public void dataXmlPUT(@Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
529 try (var xmlBody = new XmlResourceBody(body)) {
530 completeDataPUT(server.dataPUT(xmlBody, QueryParams.normalize(uriInfo)), ar);
535 * Create or replace the target data resource.
537 * @param identifier path to target
538 * @param uriInfo request URI information
539 * @param body data node for put to config DS
540 * @param ar {@link AsyncResponse} which needs to be completed
543 @Path("/data/{identifier:.+}")
545 MediaTypes.APPLICATION_YANG_DATA_XML,
546 MediaType.APPLICATION_XML,
549 public void dataXmlPUT(@Encoded @PathParam("identifier") final String identifier,
550 @Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
551 try (var xmlBody = new XmlResourceBody(body)) {
552 completeDataPUT(server.dataPUT(identifier, xmlBody, QueryParams.normalize(uriInfo)), ar);
556 private static void completeDataPUT(final RestconfFuture<DataPutResult> future, final AsyncResponse ar) {
557 future.addCallback(new JaxRsRestconfCallback<>(ar) {
559 Response transform(final DataPutResult result) {
560 return switch (result) {
561 // Note: no Location header, as it matches the request path
562 case CREATED -> Response.status(Status.CREATED).build();
563 case REPLACED -> Response.noContent().build();
570 * List RPC and action operations in RFC7951 format.
572 * @param ar {@link AsyncResponse} which needs to be completed
576 @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
577 public void operationsJsonGET(@Suspended final AsyncResponse ar) {
578 completeOperationsJsonGet(server.operationsGET(), ar);
582 * Retrieve list of operations and actions supported by the server or device in JSON format.
584 * @param operation path parameter to identify device and/or operation
585 * @param ar {@link AsyncResponse} which needs to be completed
588 @Path("/operations/{operation:.+}")
589 @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
590 public void operationsJsonGET(@PathParam("operation") final String operation, final AsyncResponse ar) {
591 completeOperationsGet(server.operationsGET(operation), ar, OperationsGetResult::toJSON);
594 private static void completeOperationsJsonGet(final RestconfFuture<OperationsGetResult> future,
595 final AsyncResponse ar) {
596 completeOperationsGet(future, ar, OperationsGetResult::toJSON);
600 * List RPC and action operations in RFC8040 XML format.
602 * @param ar {@link AsyncResponse} which needs to be completed
606 @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
607 public void operationsXmlGET(@Suspended final AsyncResponse ar) {
608 completeOperationsXmlGet(server.operationsGET(), ar);
612 * Retrieve list of operations and actions supported by the server or device in XML format.
614 * @param operation path parameter to identify device and/or operation
615 * @param ar {@link AsyncResponse} which needs to be completed
618 @Path("/operations/{operation:.+}")
619 @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
620 public void operationsXmlGET(@PathParam("operation") final String operation, final AsyncResponse ar) {
621 completeOperationsXmlGet(server.operationsGET(operation), ar);
624 private static void completeOperationsXmlGet(final RestconfFuture<OperationsGetResult> future,
625 final AsyncResponse ar) {
626 completeOperationsGet(future, ar, OperationsGetResult::toXML);
629 private static void completeOperationsGet(final RestconfFuture<OperationsGetResult> future, final AsyncResponse ar,
630 final Function<OperationsGetResult, String> toString) {
631 future.addCallback(new JaxRsRestconfCallback<OperationsGetResult>(ar) {
633 Response transform(final OperationsGetResult result) {
634 return Response.ok().entity(toString.apply(result)).build();
640 * Invoke RPC operation.
642 * @param identifier module name and rpc identifier string for the desired operation
643 * @param body the body of the operation
644 * @param uriInfo URI info
645 * @param ar {@link AsyncResponse} which needs to be completed with a {@link NormalizedNodePayload} output
648 // FIXME: identifier is just a *single* QName
649 @Path("/operations/{identifier:.+}")
651 MediaTypes.APPLICATION_YANG_DATA_XML,
652 MediaType.APPLICATION_XML,
656 MediaTypes.APPLICATION_YANG_DATA_JSON,
657 MediaTypes.APPLICATION_YANG_DATA_XML,
658 MediaType.APPLICATION_JSON,
659 MediaType.APPLICATION_XML,
662 public void operationsXmlPOST(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
663 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
664 try (var xmlBody = new XmlOperationInputBody(body)) {
665 operationsPOST(identifier, uriInfo, ar, xmlBody);
670 * Invoke RPC operation.
672 * @param identifier module name and rpc identifier string for the desired operation
673 * @param body the body of the operation
674 * @param uriInfo URI info
675 * @param ar {@link AsyncResponse} which needs to be completed with a {@link NormalizedNodePayload} output
678 // FIXME: identifier is just a *single* QName
679 @Path("/operations/{identifier:.+}")
681 MediaTypes.APPLICATION_YANG_DATA_JSON,
682 MediaType.APPLICATION_JSON,
685 MediaTypes.APPLICATION_YANG_DATA_JSON,
686 MediaTypes.APPLICATION_YANG_DATA_XML,
687 MediaType.APPLICATION_JSON,
688 MediaType.APPLICATION_XML,
691 public void operationsJsonPOST(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
692 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
693 try (var jsonBody = new JsonOperationInputBody(body)) {
694 operationsPOST(identifier, uriInfo, ar, jsonBody);
698 private void operationsPOST(final String identifier, final UriInfo uriInfo, final AsyncResponse ar,
699 final OperationInputBody body) {
700 server.operationsPOST(uriInfo.getBaseUri(), identifier, body)
701 .addCallback(new JaxRsRestconfCallback<OperationOutput>(ar) {
703 Response transform(final OperationOutput result) {
704 final var body = result.output();
705 return body == null ? Response.noContent().build()
706 : Response.ok().entity(new NormalizedNodePayload(result.operation(), body)).build();
712 * Get revision of IETF YANG Library module.
714 * @param ar {@link AsyncResponse} which needs to be completed
717 @Path("/yang-library-version")
719 MediaTypes.APPLICATION_YANG_DATA_JSON,
720 MediaTypes.APPLICATION_YANG_DATA_XML,
721 MediaType.APPLICATION_JSON,
722 MediaType.APPLICATION_XML,
725 public void yangLibraryVersionGET(@Suspended final AsyncResponse ar) {
726 server.yangLibraryVersionGET().addCallback(new JaxRsRestconfCallback<NormalizedNodePayload>(ar) {
728 Response transform(final NormalizedNodePayload result) {
729 return Response.ok().entity(result).build();