import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
-import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.restconf.api.ApiPath;
+import org.opendaylight.restconf.api.FormattableBody;
+import org.opendaylight.restconf.api.HttpStatusCode;
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;
-import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
import org.opendaylight.restconf.server.api.ConfigurationMetadata;
import org.opendaylight.restconf.server.api.CreateResourceResult;
import org.opendaylight.restconf.server.api.DataGetResult;
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.ServerRequest;
import org.opendaylight.restconf.server.api.XmlChildBody;
import org.opendaylight.restconf.server.api.XmlDataPostBody;
import org.opendaylight.restconf.server.api.XmlOperationInputBody;
};
private final @NonNull RestconfServer server;
+ private final @NonNull ServerRequest emptyRequest;
+ private final @NonNull PrettyPrintParam prettyPrint;
+ private final @NonNull ErrorTagMapping errorTagMapping;
- public JaxRsRestconf(final RestconfServer server) {
+ public JaxRsRestconf(final RestconfServer server, final ErrorTagMapping errorTagMapping,
+ final PrettyPrintParam prettyPrint) {
this.server = requireNonNull(server);
- }
+ this.errorTagMapping = requireNonNull(errorTagMapping);
+ this.prettyPrint = requireNonNull(prettyPrint);
+ emptyRequest = ServerRequest.of(QueryParameters.of(), prettyPrint);
- @Override
- @SuppressWarnings("unchecked")
- public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType,
- final Annotation[] annotations) {
- return ApiPath.class.equals(rawType) ? (ParamConverter<T>) API_PATH_CONVERTER : null;
+ LOG.info("RESTCONF data-missing condition is reported as HTTP status {}", switch (errorTagMapping) {
+ case ERRATA_5565 -> "404 (Errata 5565)";
+ case RFC8040 -> "409 (RFC8040)";
+ });
}
- private static @NonNull QueryParameters queryParams(final UriInfo uriInfo) {
+ private @NonNull ServerRequest requestOf(final UriInfo uriInfo) {
+ final QueryParameters params;
try {
- return QueryParameters.ofMultiValue(uriInfo.getQueryParameters());
+ params = QueryParameters.ofMultiValue(uriInfo.getQueryParameters());
} catch (IllegalArgumentException e) {
throw new BadRequestException(e.getMessage(), e);
}
+ return params.isEmpty() ? emptyRequest : ServerRequest.of(params, prettyPrint);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType,
+ final Annotation[] annotations) {
+ return ApiPath.class.equals(rawType) ? (ParamConverter<T>) API_PATH_CONVERTER : null;
}
/**
@SuppressWarnings("checkstyle:abbreviationAsWordInName")
public void dataDELETE(@Encoded @PathParam("identifier") final ApiPath identifier,
@Suspended final AsyncResponse ar) {
- server.dataDELETE(identifier).addCallback(new JaxRsRestconfCallback<>(ar) {
+ server.dataDELETE(emptyRequest, identifier).addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
Response transform(final Empty result) {
return Response.noContent().build();
MediaType.TEXT_XML
})
public void dataGET(@Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
- completeDataGET(server.dataGET(queryParams(uriInfo)), ar);
+ final var request = requestOf(uriInfo);
+ completeDataGET(server.dataGET(request), request.prettyPrint(), ar);
}
/**
})
public void dataGET(@Encoded @PathParam("identifier") final ApiPath identifier, @Context final UriInfo uriInfo,
@Suspended final AsyncResponse ar) {
- completeDataGET(server.dataGET(identifier, queryParams(uriInfo)), ar);
+ final var request = requestOf(uriInfo);
+ completeDataGET(server.dataGET(request, identifier), request.prettyPrint(), ar);
}
- private static void completeDataGET(final RestconfFuture<DataGetResult> future, final AsyncResponse ar) {
+ @NonNullByDefault
+ private static void completeDataGET(final RestconfFuture<DataGetResult> future, final PrettyPrintParam prettyPrint,
+ final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
Response transform(final DataGetResult result) {
- final var builder = Response.ok().entity(result.payload()).cacheControl(NO_CACHE);
+ final var builder = Response.ok()
+ .entity(new JaxRsFormattableBody(result.body(), prettyPrint))
+ .cacheControl(NO_CACHE);
fillConfigurationMetadata(builder, result);
return builder.build();
}
})
public void dataXmlPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
try (var xmlBody = new XmlResourceBody(body)) {
- completeDataPATCH(server.dataPATCH(xmlBody), ar);
+ completeDataPATCH(server.dataPATCH(emptyRequest, xmlBody), ar);
}
}
public void dataXmlPATCH(@Encoded @PathParam("identifier") final ApiPath identifier, final InputStream body,
@Suspended final AsyncResponse ar) {
try (var xmlBody = new XmlResourceBody(body)) {
- completeDataPATCH(server.dataPATCH(identifier, xmlBody), ar);
+ completeDataPATCH(server.dataPATCH(emptyRequest, identifier, xmlBody), ar);
}
}
})
public void dataJsonPATCH(final InputStream body, @Suspended final AsyncResponse ar) {
try (var jsonBody = new JsonResourceBody(body)) {
- completeDataPATCH(server.dataPATCH(jsonBody), ar);
+ completeDataPATCH(server.dataPATCH(emptyRequest, jsonBody), ar);
}
}
public void dataJsonPATCH(@Encoded @PathParam("identifier") final ApiPath identifier, final InputStream body,
@Suspended final AsyncResponse ar) {
try (var jsonBody = new JsonResourceBody(body)) {
- completeDataPATCH(server.dataPATCH(identifier, jsonBody), ar);
+ completeDataPATCH(server.dataPATCH(emptyRequest, identifier, jsonBody), ar);
}
}
public void dataYangJsonPATCH(final InputStream body, @Context final UriInfo uriInfo,
@Suspended final AsyncResponse ar) {
try (var jsonBody = new JsonPatchBody(body)) {
- completeDataYangPATCH(server.dataPATCH(queryParams(uriInfo), jsonBody), ar);
+ completeDataYangPATCH(server.dataPATCH(requestOf(uriInfo), jsonBody), ar);
}
}
public void dataYangJsonPATCH(@Encoded @PathParam("identifier") final ApiPath identifier, final InputStream body,
@Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
try (var jsonBody = new JsonPatchBody(body)) {
- completeDataYangPATCH(server.dataPATCH(identifier, queryParams(uriInfo), jsonBody), ar);
+ completeDataYangPATCH(server.dataPATCH(requestOf(uriInfo), identifier, jsonBody), ar);
}
}
public void dataYangXmlPATCH(final InputStream body, @Context final UriInfo uriInfo,
@Suspended final AsyncResponse ar) {
try (var xmlBody = new XmlPatchBody(body)) {
- completeDataYangPATCH(server.dataPATCH(queryParams(uriInfo), xmlBody), ar);
+ completeDataYangPATCH(server.dataPATCH(requestOf(uriInfo), xmlBody), ar);
}
}
public void dataYangXmlPATCH(@Encoded @PathParam("identifier") final ApiPath identifier, final InputStream body,
@Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
try (var xmlBody = new XmlPatchBody(body)) {
- completeDataYangPATCH(server.dataPATCH(identifier, queryParams(uriInfo), xmlBody), ar);
+ completeDataYangPATCH(server.dataPATCH(requestOf(uriInfo), identifier, xmlBody), ar);
}
}
- private static void completeDataYangPATCH(final RestconfFuture<DataYangPatchResult> future,
+ private void completeDataYangPATCH(final RestconfFuture<DataYangPatchResult> future,
final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
Response transform(final DataYangPatchResult result) {
- final var status = result.status();
- final var builder = Response.status(statusOf(status))
- .entity(new YangPatchStatusBody(result.params(), status));
+ final var patchStatus = result.status();
+ final var statusCode = statusOf(patchStatus);
+ final var builder = Response.status(statusCode.code(), statusCode.phrase())
+ .entity(new YangPatchStatusBody(patchStatus));
fillConfigurationMetadata(builder, result);
return builder.build();
}
- private static Status statusOf(final PatchStatusContext result) {
+ private HttpStatusCode statusOf(final PatchStatusContext result) {
if (result.ok()) {
- return Status.OK;
+ return HttpStatusCode.OK;
}
final var globalErrors = result.globalErrors();
if (globalErrors != null && !globalErrors.isEmpty()) {
}
}
}
- return Status.INTERNAL_SERVER_ERROR;
+ return HttpStatusCode.INTERNAL_SERVER_ERROR;
}
- private static Status statusOfFirst(final List<RestconfError> error) {
- return ErrorTags.statusOf(error.get(0).getErrorTag());
+ private @NonNull HttpStatusCode statusOfFirst(final List<RestconfError> error) {
+ return errorTagMapping.statusOf(error.get(0).getErrorTag());
}
});
}
public void postDataJSON(final InputStream body, @Context final UriInfo uriInfo,
@Suspended final AsyncResponse ar) {
try (var jsonBody = new JsonChildBody(body)) {
- completeDataPOST(server.dataPOST(queryParams(uriInfo), jsonBody), uriInfo, ar);
+ final var request = requestOf(uriInfo);
+ completeDataPOST(server.dataPOST(request, jsonBody), request.prettyPrint(), uriInfo, ar);
}
}
})
public void postDataJSON(@Encoded @PathParam("identifier") final ApiPath identifier, final InputStream body,
@Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
- completeDataPOST(server.dataPOST(identifier, queryParams(uriInfo), new JsonDataPostBody(body)), uriInfo, ar);
+ final var request = requestOf(uriInfo);
+ completeDataPOST(server.dataPOST(request, identifier, new JsonDataPostBody(body)), request.prettyPrint(),
+ uriInfo, ar);
}
/**
})
public void postDataXML(final InputStream body, @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
try (var xmlBody = new XmlChildBody(body)) {
- completeDataPOST(server.dataPOST(queryParams(uriInfo), xmlBody), uriInfo, ar);
+ final var request = requestOf(uriInfo);
+ completeDataPOST(server.dataPOST(request, xmlBody), request.prettyPrint(), uriInfo, ar);
}
}
})
public void postDataXML(@Encoded @PathParam("identifier") final ApiPath identifier, final InputStream body,
@Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
- completeDataPOST(server.dataPOST(identifier, queryParams(uriInfo), new XmlDataPostBody(body)), uriInfo, ar);
+ final var request = requestOf(uriInfo);
+ completeDataPOST(server.dataPOST(request, identifier, new XmlDataPostBody(body)), request.prettyPrint(),
+ uriInfo, ar);
}
- private static void completeDataPOST(final RestconfFuture<? extends DataPostResult> future, final UriInfo uriInfo,
- final AsyncResponse ar) {
+ private static void completeDataPOST(final RestconfFuture<? extends DataPostResult> future,
+ final PrettyPrintParam prettyPrint, final UriInfo uriInfo, final AsyncResponse ar) {
future.addCallback(new JaxRsRestconfCallback<DataPostResult>(ar) {
@Override
Response transform(final DataPostResult result) {
}
if (result instanceof InvokeResult invokeOperation) {
final var output = invokeOperation.output();
- return output == null ? Response.noContent().build() : Response.ok().entity(output).build();
+ return output == null ? Response.noContent().build()
+ : Response.ok().entity(new JaxRsFormattableBody(output, prettyPrint)).build();
}
LOG.error("Unhandled result {}", result);
return Response.serverError().build();
})
public void dataJsonPUT(@Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
try (var jsonBody = new JsonResourceBody(body)) {
- completeDataPUT(server.dataPUT(queryParams(uriInfo), jsonBody), ar);
+ completeDataPUT(server.dataPUT(requestOf(uriInfo), jsonBody), ar);
}
}
public void dataJsonPUT(@Encoded @PathParam("identifier") final ApiPath identifier, @Context final UriInfo uriInfo,
final InputStream body, @Suspended final AsyncResponse ar) {
try (var jsonBody = new JsonResourceBody(body)) {
- completeDataPUT(server.dataPUT(identifier, queryParams(uriInfo), jsonBody), ar);
+ completeDataPUT(server.dataPUT(requestOf(uriInfo), identifier, jsonBody), ar);
}
}
})
public void dataXmlPUT(@Context final UriInfo uriInfo, final InputStream body, @Suspended final AsyncResponse ar) {
try (var xmlBody = new XmlResourceBody(body)) {
- completeDataPUT(server.dataPUT(queryParams(uriInfo), xmlBody), ar);
+ completeDataPUT(server.dataPUT(requestOf(uriInfo), xmlBody), ar);
}
}
public void dataXmlPUT(@Encoded @PathParam("identifier") final ApiPath identifier, @Context final UriInfo uriInfo,
final InputStream body, @Suspended final AsyncResponse ar) {
try (var xmlBody = new XmlResourceBody(body)) {
- completeDataPUT(server.dataPUT(identifier, queryParams(uriInfo), xmlBody), ar);
+ completeDataPUT(server.dataPUT(requestOf(uriInfo), identifier, xmlBody), ar);
}
}
MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON
})
public void operationsGET(@Suspended final AsyncResponse ar) {
- server.operationsGET().addCallback(new FormattableBodyCallback(ar));
+ server.operationsGET(emptyRequest).addCallback(new FormattableBodyCallback(ar, prettyPrint));
}
/**
MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON
})
public void operationsGET(@PathParam("operation") final ApiPath operation, @Suspended final AsyncResponse ar) {
- server.operationsGET(operation).addCallback(new FormattableBodyCallback(ar));
+ server.operationsGET(emptyRequest, operation)
+ .addCallback(new FormattableBodyCallback(ar, prettyPrint));
}
/**
* @param identifier module name and rpc identifier string for the desired operation
* @param body the body of the operation
* @param uriInfo URI info
- * @param ar {@link AsyncResponse} which needs to be completed with a {@link NormalizedNodePayload} output
+ * @param ar {@link AsyncResponse} which needs to be completed with a {@link FormattableBody} output
*/
@POST
// FIXME: identifier is just a *single* QName
* @param identifier module name and rpc identifier string for the desired operation
* @param body the body of the operation
* @param uriInfo URI info
- * @param ar {@link AsyncResponse} which needs to be completed with a {@link NormalizedNodePayload} output
+ * @param ar {@link AsyncResponse} which needs to be completed with a {@link FormattableBody} output
*/
@POST
// FIXME: identifier is just a *single* QName
private void operationsPOST(final ApiPath identifier, final UriInfo uriInfo, final AsyncResponse ar,
final OperationInputBody body) {
- server.operationsPOST(uriInfo.getBaseUri(), identifier, queryParams(uriInfo), body)
+ server.operationsPOST(requestOf(uriInfo), uriInfo.getBaseUri(), identifier, body)
.addCallback(new JaxRsRestconfCallback<>(ar) {
@Override
Response transform(final InvokeResult result) {
MediaType.TEXT_XML
})
public void yangLibraryVersionGET(@Suspended final AsyncResponse ar) {
- server.yangLibraryVersionGET().addCallback(new FormattableBodyCallback(ar));
+ server.yangLibraryVersionGET(emptyRequest).addCallback(new FormattableBodyCallback(ar, prettyPrint));
}
// FIXME: References to these resources are generated by our yang-library implementation. That means:
@Path("/" + URLConstants.MODULES_SUBPATH + "/{fileName : [^/]+}")
public void modulesYangGET(@PathParam("fileName") final String fileName,
@QueryParam("revision") final String revision, @Suspended final AsyncResponse ar) {
- completeModulesGET(server.modulesYangGET(fileName, revision), ar);
+ completeModulesGET(server.modulesYangGET(emptyRequest, fileName, revision), ar);
}
/**
public void modulesYangGET(@Encoded @PathParam("mountPath") final ApiPath mountPath,
@PathParam("fileName") final String fileName, @QueryParam("revision") final String revision,
@Suspended final AsyncResponse ar) {
- completeModulesGET(server.modulesYangGET(mountPath, fileName, revision), ar);
+ completeModulesGET(server.modulesYangGET(emptyRequest, mountPath, fileName, revision), ar);
}
/**
@Path("/" + URLConstants.MODULES_SUBPATH + "/{fileName : [^/]+}")
public void modulesYinGET(@PathParam("fileName") final String fileName,
@QueryParam("revision") final String revision, @Suspended final AsyncResponse ar) {
- completeModulesGET(server.modulesYinGET(fileName, revision), ar);
+ completeModulesGET(server.modulesYinGET(emptyRequest, fileName, revision), ar);
}
/**
public void modulesYinGET(@Encoded @PathParam("mountPath") final ApiPath mountPath,
@PathParam("fileName") final String fileName, @QueryParam("revision") final String revision,
@Suspended final AsyncResponse ar) {
- completeModulesGET(server.modulesYinGET(mountPath, fileName, revision), ar);
+ completeModulesGET(server.modulesYinGET(emptyRequest, mountPath, fileName, revision), ar);
}
private static void completeModulesGET(final RestconfFuture<ModulesGetResult> future, final AsyncResponse ar) {