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.ApiPath;
36 import org.opendaylight.restconf.api.MediaTypes;
37 import org.opendaylight.restconf.common.errors.RestconfError;
38 import org.opendaylight.restconf.common.errors.RestconfFuture;
39 import org.opendaylight.restconf.common.patch.PatchStatusContext;
40 import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
41 import org.opendaylight.restconf.nb.rfc8040.databind.JsonChildBody;
42 import org.opendaylight.restconf.nb.rfc8040.databind.JsonDataPostBody;
43 import org.opendaylight.restconf.nb.rfc8040.databind.JsonOperationInputBody;
44 import org.opendaylight.restconf.nb.rfc8040.databind.JsonPatchBody;
45 import org.opendaylight.restconf.nb.rfc8040.databind.JsonResourceBody;
46 import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
47 import org.opendaylight.restconf.nb.rfc8040.databind.XmlChildBody;
48 import org.opendaylight.restconf.nb.rfc8040.databind.XmlDataPostBody;
49 import org.opendaylight.restconf.nb.rfc8040.databind.XmlOperationInputBody;
50 import org.opendaylight.restconf.nb.rfc8040.databind.XmlPatchBody;
51 import org.opendaylight.restconf.nb.rfc8040.databind.XmlResourceBody;
52 import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
53 import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
54 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
55 import org.opendaylight.restconf.server.api.DataPostResult;
56 import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
57 import org.opendaylight.restconf.server.api.DataPostResult.InvokeOperation;
58 import org.opendaylight.restconf.server.api.DataPutResult;
59 import org.opendaylight.restconf.server.api.OperationsGetResult;
60 import org.opendaylight.restconf.server.api.RestconfServer;
61 import org.opendaylight.restconf.server.spi.OperationOutput;
62 import org.opendaylight.yangtools.yang.common.Empty;
63 import org.opendaylight.yangtools.yang.common.Revision;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
68 * Baseline RESTCONF implementation with JAX-RS. Interfaces to a {@link RestconfServer}.
71 public final class JaxRsRestconf {
72 private static final Logger LOG = LoggerFactory.getLogger(JaxRsRestconf.class);
73 private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
75 private final RestconfServer server;
77 public JaxRsRestconf(final RestconfServer server) {
78 this.server = requireNonNull(server);
82 * Delete the target data resource.
84 * @param identifier path to target
85 * @param ar {@link AsyncResponse} which needs to be completed
88 @Path("/data/{identifier:.+}")
89 @SuppressWarnings("checkstyle:abbreviationAsWordInName")
90 public void dataDELETE(@Encoded @PathParam("identifier") final JaxRsApiPath identifier,
91 @Suspended final AsyncResponse ar) {
92 server.dataDELETE(identifier.apiPath).addCallback(new JaxRsRestconfCallback<>(ar) {
94 Response transform(final Empty result) {
95 return Response.noContent().build();
101 * Get target data resource from data root.
103 * @param uriInfo URI info
104 * @param ar {@link AsyncResponse} which needs to be completed
109 MediaTypes.APPLICATION_YANG_DATA_JSON,
110 MediaTypes.APPLICATION_YANG_DATA_XML,
111 MediaType.APPLICATION_JSON,
112 MediaType.APPLICATION_XML,
115 public void dataGET(@Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
116 final var readParams = QueryParams.newReadDataParams(uriInfo);
117 completeDataGET(server.dataGET(readParams), readParams, ar);
121 * Get target data resource.
123 * @param identifier path to target
124 * @param uriInfo URI info
125 * @param ar {@link AsyncResponse} which needs to be completed
128 @Path("/data/{identifier:.+}")
130 MediaTypes.APPLICATION_YANG_DATA_JSON,
131 MediaTypes.APPLICATION_YANG_DATA_XML,
132 MediaType.APPLICATION_JSON,
133 MediaType.APPLICATION_XML,
136 public void dataGET(@Encoded @PathParam("identifier") final JaxRsApiPath identifier,
137 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
138 final var readParams = QueryParams.newReadDataParams(uriInfo);
139 completeDataGET(server.dataGET(identifier.apiPath, readParams), readParams, ar);
142 private static void completeDataGET(final RestconfFuture<NormalizedNodePayload> future,
143 final ReadDataParams readParams, final AsyncResponse ar) {
144 future.addCallback(new JaxRsRestconfCallback<>(ar) {
146 Response transform(final NormalizedNodePayload result) {
147 return switch (readParams.content()) {
148 case ALL, CONFIG -> {
149 final var type = result.data().name().getNodeType();
150 yield Response.status(Status.OK)
152 // FIXME: is this ETag okay?
153 .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null)
154 + "-" + type.getLocalName() + '"')
155 .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC())))
158 case NONCONFIG -> Response.status(Status.OK).entity(result).build();
165 * Partially modify the target data store, as defined in
166 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
168 * @param body data node for put to config DS
169 * @param ar {@link AsyncResponse} which needs to be completed
174 MediaTypes.APPLICATION_YANG_DATA_XML,
175 MediaType.APPLICATION_XML,
178 public void dataXmlPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
179 try (var xmlBody = new XmlResourceBody(body)) {
180 completeDataPATCH(server.dataPATCH(xmlBody), ar);
185 * Partially modify the target data resource, as defined in
186 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
188 * @param identifier path to target
189 * @param body data node for put to config DS
190 * @param ar {@link AsyncResponse} which needs to be completed
193 @Path("/data/{identifier:.+}")
195 MediaTypes.APPLICATION_YANG_DATA_XML,
196 MediaType.APPLICATION_XML,
199 public void dataXmlPATCH(@Encoded @PathParam("identifier") final JaxRsApiPath identifier, final InputStream body,
200 @Suspended final AsyncResponse ar) {
201 try (var xmlBody = new XmlResourceBody(body)) {
202 completeDataPATCH(server.dataPATCH(identifier.apiPath, xmlBody), ar);
207 * Partially modify the target data store, as defined in
208 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
210 * @param body data node for put to config DS
211 * @param ar {@link AsyncResponse} which needs to be completed
216 MediaTypes.APPLICATION_YANG_DATA_JSON,
217 MediaType.APPLICATION_JSON,
219 public void dataJsonPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
220 try (var jsonBody = new JsonResourceBody(body)) {
221 completeDataPATCH(server.dataPATCH(jsonBody), ar);
226 * Partially modify the target data resource, as defined in
227 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
229 * @param identifier path to target
230 * @param body data node for put to config DS
231 * @param ar {@link AsyncResponse} which needs to be completed
234 @Path("/data/{identifier:.+}")
236 MediaTypes.APPLICATION_YANG_DATA_JSON,
237 MediaType.APPLICATION_JSON,
239 public void dataJsonPATCH(@Encoded @PathParam("identifier") final JaxRsApiPath identifier, final InputStream body,
240 @Suspended final AsyncResponse ar) {
241 try (var jsonBody = new JsonResourceBody(body)) {
242 completeDataPATCH(server.dataPATCH(identifier.apiPath, jsonBody), ar);
246 private static void completeDataPATCH(final RestconfFuture<Empty> future, final AsyncResponse ar) {
247 future.addCallback(new JaxRsRestconfCallback<>(ar) {
249 Response transform(final Empty result) {
250 return Response.ok().build();
256 * Ordered list of edits that are applied to the datastore by the server, as defined in
257 * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2">RFC8072, section 2</a>.
259 * @param body YANG Patch body
260 * @param ar {@link AsyncResponse} which needs to be completed with a {@link PatchStatusContext}
264 @Consumes(MediaTypes.APPLICATION_YANG_PATCH_JSON)
266 MediaTypes.APPLICATION_YANG_DATA_JSON,
267 MediaTypes.APPLICATION_YANG_DATA_XML
269 public void dataYangJsonPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
270 try (var jsonBody = new JsonPatchBody(body)) {
271 completeDataYangPATCH(server.dataPATCH(jsonBody), ar);
276 * Ordered list of edits that are applied to the target datastore by the server, as defined in
277 * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2">RFC8072, section 2</a>.
279 * @param identifier path to target
280 * @param body YANG Patch body
281 * @param ar {@link AsyncResponse} which needs to be completed with a {@link PatchStatusContext}
284 @Path("/data/{identifier:.+}")
285 @Consumes(MediaTypes.APPLICATION_YANG_PATCH_JSON)
287 MediaTypes.APPLICATION_YANG_DATA_JSON,
288 MediaTypes.APPLICATION_YANG_DATA_XML
290 public void dataYangJsonPATCH(@Encoded @PathParam("identifier") final JaxRsApiPath identifier,
291 final InputStream body, @Suspended final AsyncResponse ar) {
292 try (var jsonBody = new JsonPatchBody(body)) {
293 completeDataYangPATCH(server.dataPATCH(identifier.apiPath, jsonBody), ar);
298 * Ordered list of edits that are applied to the datastore by the server, as defined in
299 * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2">RFC8072, section 2</a>.
301 * @param body YANG Patch body
302 * @param ar {@link AsyncResponse} which needs to be completed with a {@link PatchStatusContext}
306 @Consumes(MediaTypes.APPLICATION_YANG_PATCH_XML)
308 MediaTypes.APPLICATION_YANG_DATA_JSON,
309 MediaTypes.APPLICATION_YANG_DATA_XML
311 public void dataYangXmlPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
312 try (var xmlBody = new XmlPatchBody(body)) {
313 completeDataYangPATCH(server.dataPATCH(xmlBody), ar);
318 * Ordered list of edits that are applied to the target datastore by the server, as defined in
319 * <a href="https://www.rfc-editor.org/rfc/rfc8072#section-2">RFC8072, section 2</a>.
321 * @param identifier path to target
322 * @param body YANG Patch body
323 * @param ar {@link AsyncResponse} which needs to be completed with a {@link PatchStatusContext}
326 @Path("/data/{identifier:.+}")
327 @Consumes(MediaTypes.APPLICATION_YANG_PATCH_XML)
329 MediaTypes.APPLICATION_YANG_DATA_JSON,
330 MediaTypes.APPLICATION_YANG_DATA_XML
332 public void dataYangXmlPATCH(@Encoded @PathParam("identifier") final JaxRsApiPath identifier,
333 final InputStream body, @Suspended final AsyncResponse ar) {
334 try (var xmlBody = new XmlPatchBody(body)) {
335 completeDataYangPATCH(server.dataPATCH(identifier.apiPath, xmlBody), ar);
339 private static void completeDataYangPATCH(final RestconfFuture<PatchStatusContext> future, final AsyncResponse ar) {
340 future.addCallback(new JaxRsRestconfCallback<>(ar) {
342 Response transform(final PatchStatusContext result) {
343 return Response.status(statusOf(result)).entity(result).build();
346 private static Status statusOf(final PatchStatusContext result) {
350 final var globalErrors = result.globalErrors();
351 if (globalErrors != null && !globalErrors.isEmpty()) {
352 return statusOfFirst(globalErrors);
354 for (var edit : result.editCollection()) {
356 final var editErrors = edit.getEditErrors();
357 if (editErrors != null && !editErrors.isEmpty()) {
358 return statusOfFirst(editErrors);
362 return Status.INTERNAL_SERVER_ERROR;
365 private static Status statusOfFirst(final List<RestconfError> error) {
366 return ErrorTags.statusOf(error.get(0).getErrorTag());
372 * Create a top-level data resource.
374 * @param body data node for put to config DS
375 * @param uriInfo URI info
376 * @param ar {@link AsyncResponse} which needs to be completed
381 MediaTypes.APPLICATION_YANG_DATA_JSON,
382 MediaType.APPLICATION_JSON,
384 public void postDataJSON(final InputStream body, @Context final UriInfo uriInfo,
385 @Suspended final AsyncResponse ar) {
386 try (var jsonBody = new JsonChildBody(body)) {
387 completeDataPOST(server.dataPOST(jsonBody, QueryParams.normalize(uriInfo)), uriInfo, ar);
392 * Create a data resource in target.
394 * @param identifier path to target
395 * @param body data node for put to config DS
396 * @param uriInfo URI info
397 * @param ar {@link AsyncResponse} which needs to be completed
400 @Path("/data/{identifier:.+}")
402 MediaTypes.APPLICATION_YANG_DATA_JSON,
403 MediaType.APPLICATION_JSON,
405 public void postDataJSON(@Encoded @PathParam("identifier") final JaxRsApiPath identifier, final InputStream body,
406 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
407 completeDataPOST(server.dataPOST(identifier.apiPath, new JsonDataPostBody(body),
408 QueryParams.normalize(uriInfo)), uriInfo, ar);
412 * Create a top-level data resource.
414 * @param body data node for put to config DS
415 * @param uriInfo URI info
416 * @param ar {@link AsyncResponse} which needs to be completed
421 MediaTypes.APPLICATION_YANG_DATA_XML,
422 MediaType.APPLICATION_XML,
425 public void postDataXML(final InputStream body, @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
426 try (var xmlBody = new XmlChildBody(body)) {
427 completeDataPOST(server.dataPOST(xmlBody, QueryParams.normalize(uriInfo)), uriInfo, ar);
432 * Create a data resource in target.
434 * @param identifier path to target
435 * @param body data node for put to config DS
436 * @param uriInfo URI info
437 * @param ar {@link AsyncResponse} which needs to be completed
440 @Path("/data/{identifier:.+}")
442 MediaTypes.APPLICATION_YANG_DATA_XML,
443 MediaType.APPLICATION_XML,
446 public void postDataXML(@Encoded @PathParam("identifier") final JaxRsApiPath identifier, final InputStream body,
447 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
448 completeDataPOST(server.dataPOST(identifier.apiPath, new XmlDataPostBody(body), QueryParams.normalize(uriInfo)),
452 private static void completeDataPOST(final RestconfFuture<? extends DataPostResult> future, final UriInfo uriInfo,
453 final AsyncResponse ar) {
454 future.addCallback(new JaxRsRestconfCallback<DataPostResult>(ar) {
456 Response transform(final DataPostResult result) {
457 if (result instanceof CreateResource createResource) {
458 return Response.created(uriInfo.getBaseUriBuilder()
460 .path(createResource.createdPath())
464 if (result instanceof InvokeOperation invokeOperation) {
465 final var output = invokeOperation.output();
466 return output == null ? Response.status(Status.NO_CONTENT).build()
467 : Response.status(Status.OK).entity(output).build();
469 LOG.error("Unhandled result {}", result);
470 return Response.serverError().build();
476 * Replace the data store.
478 * @param uriInfo request URI information
479 * @param body data node for put to config DS
480 * @param ar {@link AsyncResponse} which needs to be completed
485 MediaTypes.APPLICATION_YANG_DATA_JSON,
486 MediaType.APPLICATION_JSON,
488 public void dataJsonPUT(@Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
489 try (var jsonBody = new JsonResourceBody(body)) {
490 completeDataPUT(server.dataPUT(jsonBody, QueryParams.normalize(uriInfo)), ar);
495 * Create or replace the target data resource.
497 * @param identifier path to target
498 * @param uriInfo request URI information
499 * @param body data node for put to config DS
500 * @param ar {@link AsyncResponse} which needs to be completed
503 @Path("/data/{identifier:.+}")
505 MediaTypes.APPLICATION_YANG_DATA_JSON,
506 MediaType.APPLICATION_JSON,
508 public void dataJsonPUT(@Encoded @PathParam("identifier") final JaxRsApiPath identifier,
509 @Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
510 try (var jsonBody = new JsonResourceBody(body)) {
511 completeDataPUT(server.dataPUT(identifier.apiPath, jsonBody, QueryParams.normalize(uriInfo)), ar);
516 * Replace the data store.
518 * @param uriInfo request URI information
519 * @param body data node for put to config DS
520 * @param ar {@link AsyncResponse} which needs to be completed
525 MediaTypes.APPLICATION_YANG_DATA_XML,
526 MediaType.APPLICATION_XML,
529 public void dataXmlPUT(@Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
530 try (var xmlBody = new XmlResourceBody(body)) {
531 completeDataPUT(server.dataPUT(xmlBody, QueryParams.normalize(uriInfo)), ar);
536 * Create or replace the target data resource.
538 * @param identifier path to target
539 * @param uriInfo request URI information
540 * @param body data node for put to config DS
541 * @param ar {@link AsyncResponse} which needs to be completed
544 @Path("/data/{identifier:.+}")
546 MediaTypes.APPLICATION_YANG_DATA_XML,
547 MediaType.APPLICATION_XML,
550 public void dataXmlPUT(@Encoded @PathParam("identifier") final JaxRsApiPath identifier,
551 @Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
552 try (var xmlBody = new XmlResourceBody(body)) {
553 completeDataPUT(server.dataPUT(identifier.apiPath, xmlBody, QueryParams.normalize(uriInfo)), ar);
557 private static void completeDataPUT(final RestconfFuture<DataPutResult> future, final AsyncResponse ar) {
558 future.addCallback(new JaxRsRestconfCallback<>(ar) {
560 Response transform(final DataPutResult result) {
561 return switch (result) {
562 // Note: no Location header, as it matches the request path
563 case CREATED -> Response.status(Status.CREATED).build();
564 case REPLACED -> Response.noContent().build();
571 * List RPC and action operations in RFC7951 format.
573 * @param ar {@link AsyncResponse} which needs to be completed
577 @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
578 public void operationsJsonGET(@Suspended final AsyncResponse ar) {
579 completeOperationsJsonGet(server.operationsGET(), ar);
583 * Retrieve list of operations and actions supported by the server or device in JSON format.
585 * @param operation path parameter to identify device and/or operation
586 * @param ar {@link AsyncResponse} which needs to be completed
589 @Path("/operations/{operation:.+}")
590 @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
591 public void operationsJsonGET(@PathParam("operation") final JaxRsApiPath operation, final AsyncResponse ar) {
592 completeOperationsGet(server.operationsGET(operation.apiPath), ar, OperationsGetResult::toJSON);
595 private static void completeOperationsJsonGet(final RestconfFuture<OperationsGetResult> future,
596 final AsyncResponse ar) {
597 completeOperationsGet(future, ar, OperationsGetResult::toJSON);
601 * List RPC and action operations in RFC8040 XML format.
603 * @param ar {@link AsyncResponse} which needs to be completed
607 @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
608 public void operationsXmlGET(@Suspended final AsyncResponse ar) {
609 completeOperationsXmlGet(server.operationsGET(), ar);
613 * Retrieve list of operations and actions supported by the server or device in XML format.
615 * @param operation path parameter to identify device and/or operation
616 * @param ar {@link AsyncResponse} which needs to be completed
619 @Path("/operations/{operation:.+}")
620 @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
621 public void operationsXmlGET(@PathParam("operation") final JaxRsApiPath operation, final AsyncResponse ar) {
622 completeOperationsXmlGet(server.operationsGET(operation.apiPath), ar);
625 private static void completeOperationsXmlGet(final RestconfFuture<OperationsGetResult> future,
626 final AsyncResponse ar) {
627 completeOperationsGet(future, ar, OperationsGetResult::toXML);
630 private static void completeOperationsGet(final RestconfFuture<OperationsGetResult> future, final AsyncResponse ar,
631 final Function<OperationsGetResult, String> toString) {
632 future.addCallback(new JaxRsRestconfCallback<OperationsGetResult>(ar) {
634 Response transform(final OperationsGetResult result) {
635 return Response.ok().entity(toString.apply(result)).build();
641 * Invoke RPC operation.
643 * @param identifier module name and rpc identifier string for the desired operation
644 * @param body the body of the operation
645 * @param uriInfo URI info
646 * @param ar {@link AsyncResponse} which needs to be completed with a {@link NormalizedNodePayload} output
649 // FIXME: identifier is just a *single* QName
650 @Path("/operations/{identifier:.+}")
652 MediaTypes.APPLICATION_YANG_DATA_XML,
653 MediaType.APPLICATION_XML,
657 MediaTypes.APPLICATION_YANG_DATA_JSON,
658 MediaTypes.APPLICATION_YANG_DATA_XML,
659 MediaType.APPLICATION_JSON,
660 MediaType.APPLICATION_XML,
663 public void operationsXmlPOST(@Encoded @PathParam("identifier") final JaxRsApiPath identifier,
664 final InputStream body, @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
665 try (var xmlBody = new XmlOperationInputBody(body)) {
666 operationsPOST(identifier.apiPath, uriInfo, ar, xmlBody);
671 * Invoke RPC operation.
673 * @param identifier module name and rpc identifier string for the desired operation
674 * @param body the body of the operation
675 * @param uriInfo URI info
676 * @param ar {@link AsyncResponse} which needs to be completed with a {@link NormalizedNodePayload} output
679 // FIXME: identifier is just a *single* QName
680 @Path("/operations/{identifier:.+}")
682 MediaTypes.APPLICATION_YANG_DATA_JSON,
683 MediaType.APPLICATION_JSON,
686 MediaTypes.APPLICATION_YANG_DATA_JSON,
687 MediaTypes.APPLICATION_YANG_DATA_XML,
688 MediaType.APPLICATION_JSON,
689 MediaType.APPLICATION_XML,
692 public void operationsJsonPOST(@Encoded @PathParam("identifier") final JaxRsApiPath identifier,
693 final InputStream body, @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
694 try (var jsonBody = new JsonOperationInputBody(body)) {
695 operationsPOST(identifier.apiPath, uriInfo, ar, jsonBody);
699 private void operationsPOST(final ApiPath identifier, final UriInfo uriInfo, final AsyncResponse ar,
700 final OperationInputBody body) {
701 server.operationsPOST(uriInfo.getBaseUri(), identifier, body)
702 .addCallback(new JaxRsRestconfCallback<OperationOutput>(ar) {
704 Response transform(final OperationOutput result) {
705 final var body = result.output();
706 return body == null ? Response.noContent().build()
707 : Response.ok().entity(new NormalizedNodePayload(result.operation(), body)).build();
713 * Get revision of IETF YANG Library module.
715 * @param ar {@link AsyncResponse} which needs to be completed
718 @Path("/yang-library-version")
720 MediaTypes.APPLICATION_YANG_DATA_JSON,
721 MediaTypes.APPLICATION_YANG_DATA_XML,
722 MediaType.APPLICATION_JSON,
723 MediaType.APPLICATION_XML,
726 public void yangLibraryVersionGET(@Suspended final AsyncResponse ar) {
727 server.yangLibraryVersionGET().addCallback(new JaxRsRestconfCallback<NormalizedNodePayload>(ar) {
729 Response transform(final NormalizedNodePayload result) {
730 return Response.ok().entity(result).build();