import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.io.CharSource;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
import org.opendaylight.restconf.api.ApiPath;
+import org.opendaylight.restconf.api.FormattableBody;
import org.opendaylight.restconf.api.query.ContentParam;
import org.opendaylight.restconf.api.query.WithDefaultsParam;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.errors.RestconfFuture;
import org.opendaylight.restconf.common.errors.SettableRestconfFuture;
import org.opendaylight.restconf.common.patch.PatchContext;
-import org.opendaylight.restconf.common.patch.PatchStatusContext;
-import org.opendaylight.restconf.common.patch.PatchStatusEntity;
import org.opendaylight.restconf.nb.rfc8040.Insert;
import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
-import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
-import org.opendaylight.restconf.nb.rfc8040.utils.parser.YangInstanceIdentifierSerializer;
import org.opendaylight.restconf.server.api.ChildBody;
import org.opendaylight.restconf.server.api.ConfigurationMetadata;
+import org.opendaylight.restconf.server.api.CreateResourceResult;
import org.opendaylight.restconf.server.api.DataGetParams;
import org.opendaylight.restconf.server.api.DataGetResult;
-import org.opendaylight.restconf.server.api.DataPatchPath;
import org.opendaylight.restconf.server.api.DataPatchResult;
import org.opendaylight.restconf.server.api.DataPostBody;
-import org.opendaylight.restconf.server.api.DataPostPath;
import org.opendaylight.restconf.server.api.DataPostResult;
-import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
-import org.opendaylight.restconf.server.api.DataPostResult.InvokeOperation;
-import org.opendaylight.restconf.server.api.DataPutPath;
import org.opendaylight.restconf.server.api.DataPutResult;
import org.opendaylight.restconf.server.api.DataYangPatchResult;
import org.opendaylight.restconf.server.api.DatabindContext;
+import org.opendaylight.restconf.server.api.DatabindPath;
+import org.opendaylight.restconf.server.api.DatabindPath.Action;
+import org.opendaylight.restconf.server.api.DatabindPath.Data;
+import org.opendaylight.restconf.server.api.DatabindPath.InstanceReference;
+import org.opendaylight.restconf.server.api.DatabindPath.OperationPath;
+import org.opendaylight.restconf.server.api.DatabindPath.Rpc;
+import org.opendaylight.restconf.server.api.InvokeResult;
import org.opendaylight.restconf.server.api.OperationInputBody;
-import org.opendaylight.restconf.server.api.OperationsGetResult;
-import org.opendaylight.restconf.server.api.OperationsPostPath;
-import org.opendaylight.restconf.server.api.OperationsPostResult;
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.ServerRequest;
+import org.opendaylight.restconf.server.spi.ApiPathCanonizer;
import org.opendaylight.restconf.server.spi.ApiPathNormalizer;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.DataPath;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.InstanceReference;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.OperationPath;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.OperationPath.Rpc;
+import org.opendaylight.restconf.server.spi.DefaultResourceContext;
+import org.opendaylight.restconf.server.spi.HttpGetResource;
+import org.opendaylight.restconf.server.spi.NormalizedFormattableBody;
+import org.opendaylight.restconf.server.spi.NormalizedNodeWriterFactory;
import org.opendaylight.restconf.server.spi.OperationInput;
+import org.opendaylight.restconf.server.spi.OperationOutputBody;
+import org.opendaylight.restconf.server.spi.OperationsResource;
import org.opendaylight.restconf.server.spi.RpcImplementation;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.with.defaults.rev110601.WithDefaultsMode;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.common.XMLNamespace;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
import org.opendaylight.yangtools.yang.model.api.source.YinTextSource;
import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
-import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// operations. This should be handled through proper allocation indirection.
public abstract class RestconfStrategy {
@NonNullByDefault
- public record StrategyAndPath(RestconfStrategy strategy, DataPath path) {
+ public record StrategyAndPath(RestconfStrategy strategy, Data path) {
public StrategyAndPath {
requireNonNull(strategy);
requireNonNull(path);
private final DOMMountPointService mountPointService;
private final DOMActionService actionService;
private final DOMRpcService rpcService;
+ private final HttpGetResource operations;
RestconfStrategy(final DatabindContext databind, final ImmutableMap<QName, RpcImplementation> localRpcs,
final @Nullable DOMRpcService rpcService, final @Nullable DOMActionService actionService,
- final YangTextSourceExtension sourceProvider, final @Nullable DOMMountPointService mountPointService) {
+ final @Nullable YangTextSourceExtension sourceProvider,
+ final @Nullable DOMMountPointService mountPointService) {
this.databind = requireNonNull(databind);
this.localRpcs = requireNonNull(localRpcs);
this.rpcService = rpcService;
this.sourceProvider = sourceProvider;
this.mountPointService = mountPointService;
pathNormalizer = new ApiPathNormalizer(databind);
+ operations = new OperationsResource(pathNormalizer);
}
public final @NonNull StrategyAndPath resolveStrategyPath(final ApiPath path) {
}
final var dataBroker = mountPoint.getService(DOMDataBroker.class);
if (dataBroker.isPresent()) {
- return new MdsalRestconfStrategy(mountDatabind, dataBroker.orElseThrow(), rpcService, actionService,
- sourceProvider, mountPointService);
+ return new MdsalRestconfStrategy(mountDatabind, dataBroker.orElseThrow(), ImmutableMap.of(), rpcService,
+ actionService, sourceProvider, mountPointService);
}
LOG.warn("Mount point {} does not expose a suitable access interface", mountPath);
throw new RestconfDocumentedException("Could not find a supported access interface in mount point",
}, MoreExecutors.directExecutor());
}
- public @NonNull RestconfFuture<DataPutResult> dataPUT(final ApiPath apiPath, final ResourceBody body,
- final Map<String, String> queryParameters) {
- final DataPath path;
+ public @NonNull RestconfFuture<DataPutResult> dataPUT(final ServerRequest request, final ApiPath apiPath,
+ final ResourceBody body) {
+ final Data path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
} catch (RestconfDocumentedException e) {
final Insert insert;
try {
- insert = Insert.ofQueryParameters(databind, queryParameters);
+ insert = Insert.of(databind, request.queryParameters());
} catch (IllegalArgumentException e) {
return RestconfFuture.failed(new RestconfDocumentedException(e.getMessage(),
ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e));
}
final NormalizedNode data;
try {
- data = body.toNormalizedNode(new DataPutPath(databind, path.inference(), path.instance()));
+ data = body.toNormalizedNode(path);
} catch (RestconfDocumentedException e) {
return RestconfFuture.failed(e);
}
* @param insert {@link Insert}
* @return A {@link RestconfFuture}
*/
- public final @NonNull RestconfFuture<CreateResource> postData(final YangInstanceIdentifier path,
+ public final @NonNull RestconfFuture<CreateResourceResult> postData(final YangInstanceIdentifier path,
final NormalizedNode data, final @Nullable Insert insert) {
final ListenableFuture<? extends CommitInfo> future;
if (insert != null) {
future = createAndCommit(prepareWriteExecution(), path, data);
}
- final var ret = new SettableRestconfFuture<CreateResource>();
+ final var ret = new SettableRestconfFuture<CreateResourceResult>();
Futures.addCallback(future, new FutureCallback<CommitInfo>() {
@Override
public void onSuccess(final CommitInfo result) {
- ret.set(new CreateResource(new YangInstanceIdentifierSerializer(databind).serializePath(
+ ret.set(new CreateResourceResult(new ApiPathCanonizer(databind).dataToApiPath(
data instanceof MapNode mapData && !mapData.isEmpty()
? path.node(mapData.body().iterator().next().name()) : path)));
}
* @throws NullPointerException if any argument is {@code null}
*/
public final @NonNull RestconfFuture<DataPatchResult> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
- final DataPath path;
+ final Data path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
} catch (RestconfDocumentedException e) {
final NormalizedNode data;
try {
- data = body.toNormalizedNode(new DataPutPath(databind, path.inference(), path.instance()));
+ data = body.toNormalizedNode(path);
} catch (RestconfDocumentedException e) {
return RestconfFuture.failed(e);
}
}
public final @NonNull RestconfFuture<DataYangPatchResult> dataPATCH(final ApiPath apiPath, final PatchBody body) {
- final DataPath path;
+ final Data path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
} catch (RestconfDocumentedException e) {
final PatchContext patch;
try {
- patch = body.toPatchContext(new DataPatchPath(databind, path.instance()));
+ patch = body.toPatchContext(new DefaultResourceContext(path));
} catch (IOException e) {
LOG.debug("Error parsing YANG Patch input", e);
return RestconfFuture.failed(new RestconfDocumentedException("Error parsing input: " + e.getMessage(),
* @param patch Patch context to be processed
* @return {@link PatchStatusContext}
*/
+ @VisibleForTesting
public final @NonNull RestconfFuture<DataYangPatchResult> patchData(final PatchContext patch) {
final var editCollection = new ArrayList<PatchStatusEntity>();
final var tx = prepareWriteExecution();
if (!noError) {
tx.cancel();
ret.set(new DataYangPatchResult(
- new PatchStatusContext(modelContext(), patch.patchId(), List.copyOf(editCollection), false, null)));
+ new PatchStatusContext(databind(), patch.patchId(), List.copyOf(editCollection), false, null)));
return ret;
}
@Override
public void onSuccess(final CommitInfo result) {
ret.set(new DataYangPatchResult(
- new PatchStatusContext(modelContext(), patch.patchId(), List.copyOf(editCollection), true, null)));
+ new PatchStatusContext(databind(), 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(modelContext(), patch.patchId(), List.copyOf(editCollection), false,
+ new PatchStatusContext(databind(), patch.patchId(), List.copyOf(editCollection), false,
TransactionUtil.decodeException(cause, "PATCH", null, modelContext()).getErrors())));
}
}, MoreExecutors.directExecutor());
return ret;
}
- private void insertWithPointPost(final RestconfTransaction tx, final YangInstanceIdentifier path,
+ private static void insertWithPointPost(final RestconfTransaction tx, final YangInstanceIdentifier path,
final NormalizedNode data, final PathArgument pointArg, final NormalizedNodeContainer<?> readList,
final boolean before) {
tx.remove(path);
* @return A {@link RestconfFuture}
* @throws NullPointerException if {@code apiPath} is {@code null}
*/
+ @NonNullByDefault
@SuppressWarnings("checkstyle:abbreviationAsWordInName")
- public final @NonNull RestconfFuture<Empty> dataDELETE(final ApiPath apiPath) {
- final DataPath path;
+ public final RestconfFuture<Empty> dataDELETE(final ServerRequest request, final ApiPath apiPath) {
+ final Data path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
} catch (RestconfDocumentedException e) {
// FIXME: reject empty YangInstanceIdentifier, as datastores may not be deleted
final var ret = new SettableRestconfFuture<Empty>();
- delete(ret, path.instance());
+ delete(ret, request, path.instance());
return ret;
}
- abstract void delete(@NonNull SettableRestconfFuture<Empty> future, @NonNull YangInstanceIdentifier path);
+ @NonNullByDefault
+ abstract void delete(SettableRestconfFuture<Empty> future, ServerRequest request, YangInstanceIdentifier path);
- public final @NonNull RestconfFuture<DataGetResult> dataGET(final ApiPath apiPath,
- final DataGetParams params) {
- final DataPath path;
+ public final @NonNull RestconfFuture<DataGetResult> dataGET(final ServerRequest request, final ApiPath apiPath) {
+ final Data path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
} catch (RestconfDocumentedException e) {
return RestconfFuture.failed(e);
}
- return dataGET(path, params);
+
+ final DataGetParams getParams;
+ try {
+ getParams = DataGetParams.of(request.queryParameters());
+ } catch (IllegalArgumentException e) {
+ return RestconfFuture.failed(new RestconfDocumentedException(e,
+ new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid GET /data parameters", null,
+ e.getMessage())));
+ }
+ return dataGET(request, path, getParams);
}
- abstract @NonNull RestconfFuture<DataGetResult> dataGET(DataPath path, DataGetParams params);
+ abstract @NonNull RestconfFuture<DataGetResult> dataGET(ServerRequest request, Data path, DataGetParams params);
- static final @NonNull RestconfFuture<DataGetResult> completeDataGET(final Inference inference,
- final QueryParameters queryParams, final @Nullable NormalizedNode node,
- final @Nullable ConfigurationMetadata metadata) {
+ @NonNullByDefault
+ static final RestconfFuture<DataGetResult> completeDataGET(final @Nullable NormalizedNode node, final Data path,
+ final NormalizedNodeWriterFactory writerFactory, final @Nullable ConfigurationMetadata metadata) {
+ // Non-existing data
if (node == null) {
return RestconfFuture.failed(new RestconfDocumentedException(
"Request could not be completed because the relevant data model content does not exist",
ErrorType.PROTOCOL, ErrorTag.DATA_MISSING));
}
- final var payload = new NormalizedNodePayload(inference, node, queryParams);
- return RestconfFuture.of(metadata == null ? new DataGetResult(payload)
- : new DataGetResult(payload, metadata.entityTag(), metadata.lastModified()));
+ final var body = NormalizedFormattableBody.of(path, node, writerFactory);
+ return RestconfFuture.of(metadata == null ? new DataGetResult(body)
+ : new DataGetResult(body, metadata.entityTag(), metadata.lastModified()));
}
/**
y -> builder.addChild((T) prepareData(y.getValue(), stateMap.get(y.getKey()))));
}
- public @NonNull RestconfFuture<OperationsGetResult> operationsGET() {
- final var modelContext = modelContext();
- final var modules = modelContext.getModuleStatements();
- if (modules.isEmpty()) {
- // No modules, or defensive return empty content
- return RestconfFuture.of(new OperationsGetResult.Container(modelContext, ImmutableSetMultimap.of()));
- }
-
- // RPC QNames by their XMLNamespace/Revision. This should be a Table, but Revision can be null, which wrecks us.
- final var table = new HashMap<XMLNamespace, Map<Revision, ImmutableSet<QName>>>();
- for (var entry : modules.entrySet()) {
- final var module = entry.getValue();
- final var rpcNames = module.streamEffectiveSubstatements(RpcEffectiveStatement.class)
- .map(RpcEffectiveStatement::argument)
- .collect(ImmutableSet.toImmutableSet());
- if (!rpcNames.isEmpty()) {
- final var namespace = entry.getKey();
- table.computeIfAbsent(namespace.namespace(), ignored -> new HashMap<>())
- .put(namespace.revision(), rpcNames);
- }
- }
-
- // Now pick the latest revision for each namespace
- final var rpcs = ImmutableSetMultimap.<QNameModule, QName>builder();
- for (var entry : table.entrySet()) {
- entry.getValue().entrySet().stream()
- .sorted(Comparator.comparing(Entry::getKey, (first, second) -> Revision.compare(second, first)))
- .findFirst()
- .ifPresent(row -> rpcs.putAll(QNameModule.of(entry.getKey(), row.getKey()), row.getValue()));
- }
- return RestconfFuture.of(new OperationsGetResult.Container(modelContext, rpcs.build()));
+ @NonNullByDefault
+ public RestconfFuture<FormattableBody> operationsGET(final ServerRequest request) {
+ return operations.httpGET(request);
}
- public @NonNull RestconfFuture<OperationsGetResult> operationsGET(final ApiPath apiPath) {
- if (apiPath.steps().isEmpty()) {
- return operationsGET();
- }
-
- final Rpc rpc;
- try {
- rpc = pathNormalizer.normalizeRpcPath(apiPath);
- } catch (RestconfDocumentedException e) {
- return RestconfFuture.failed(e);
- }
-
- return RestconfFuture.of(new OperationsGetResult.Leaf(rpc.inference().modelContext(), rpc.rpc().argument()));
+ @NonNullByDefault
+ public RestconfFuture<FormattableBody> operationsGET(final ServerRequest request, final ApiPath apiPath) {
+ return operations.httpGET(request, apiPath);
}
- public @NonNull RestconfFuture<OperationsPostResult> operationsPOST(final URI restconfURI, final ApiPath apiPath,
- final OperationInputBody body) {
- final OperationPath.Rpc path;
+ public @NonNull RestconfFuture<InvokeResult> operationsPOST(final ServerRequest request, final URI restconfURI,
+ final ApiPath apiPath, final OperationInputBody body) {
+ final Rpc path;
try {
path = pathNormalizer.normalizeRpcPath(apiPath);
} catch (RestconfDocumentedException e) {
return RestconfFuture.failed(e);
}
- final var postPath = new OperationsPostPath(databind, path.inference());
final ContainerNode data;
try {
- data = body.toContainerNode(postPath);
+ data = body.toContainerNode(path);
} catch (IOException e) {
LOG.debug("Error reading input", e);
return RestconfFuture.failed(new RestconfDocumentedException("Error parsing input: " + e.getMessage(),
final var type = path.rpc().argument();
final var local = localRpcs.get(type);
if (local != null) {
- return local.invoke(restconfURI, new OperationInput(databind, postPath.operation(), data));
+ return local.invoke(restconfURI, new OperationInput(path, data))
+ .transform(output -> outputToInvokeResult(path, output));
}
if (rpcService == null) {
LOG.debug("RPC invocation is not available");
ErrorType.PROTOCOL, ErrorTag.OPERATION_NOT_SUPPORTED));
}
- final var ret = new SettableRestconfFuture<OperationsPostResult>();
+ final var ret = new SettableRestconfFuture<InvokeResult>();
Futures.addCallback(rpcService.invokeRpc(type, data), new FutureCallback<DOMRpcResult>() {
@Override
public void onSuccess(final DOMRpcResult response) {
final var errors = response.errors();
if (errors.isEmpty()) {
- ret.set(new OperationsPostResult(databind, postPath.operation(), response.value()));
+ ret.set(outputToInvokeResult(path, response.value()));
} else {
LOG.debug("RPC invocation reported {}", response.errors());
ret.setFailure(new RestconfDocumentedException("RPC implementation reported errors", null,
return ret;
}
+ private static @NonNull InvokeResult outputToInvokeResult(final @NonNull OperationPath path,
+ final @Nullable ContainerNode value) {
+ return value == null || value.isEmpty() ? InvokeResult.EMPTY
+ : new InvokeResult(new OperationOutputBody(path, value));
+ }
+
public @NonNull RestconfFuture<CharSource> resolveSource(final SourceIdentifier source,
final Class<? extends SourceRepresentation> representation) {
final var src = requireNonNull(source);
ErrorType.APPLICATION, ErrorTag.DATA_MISSING));
}
- public final @NonNull RestconfFuture<? extends DataPostResult> dataPOST(final ApiPath apiPath,
- final DataPostBody body, final Map<String, String> queryParameters) {
- if (apiPath.steps().isEmpty()) {
- return dataCreatePOST(body.toResource(), queryParameters);
+ public final @NonNull RestconfFuture<? extends DataPostResult> dataPOST(final ServerRequest request,
+ final ApiPath apiPath, final DataPostBody body) {
+ if (apiPath.isEmpty()) {
+ return dataCreatePOST(request, body.toResource());
}
final InstanceReference path;
try {
} catch (RestconfDocumentedException e) {
return RestconfFuture.failed(e);
}
- if (path instanceof DataPath dataPath) {
+ if (path instanceof Data dataPath) {
try (var resourceBody = body.toResource()) {
- return dataCreatePOST(new DataPostPath(databind, dataPath.inference(), dataPath.instance()),
- resourceBody, queryParameters);
+ return dataCreatePOST(request, dataPath, resourceBody);
}
}
- if (path instanceof OperationPath.Action actionPath) {
+ if (path instanceof Action actionPath) {
try (var inputBody = body.toOperationInput()) {
return dataInvokePOST(actionPath, inputBody);
}
return RestconfFuture.failed(new RestconfDocumentedException("Unhandled path " + path));
}
- public @NonNull RestconfFuture<CreateResource> dataCreatePOST(final ChildBody body,
- final Map<String, String> queryParameters) {
- return dataCreatePOST(new DataPostPath(databind,
- SchemaInferenceStack.of(databind.modelContext()).toInference(), YangInstanceIdentifier.of()), body,
- queryParameters);
+ public @NonNull RestconfFuture<CreateResourceResult> dataCreatePOST(final ServerRequest request,
+ final ChildBody body) {
+ return dataCreatePOST(request, new DatabindPath.Data(databind), body);
}
- private @NonNull RestconfFuture<CreateResource> dataCreatePOST(final DataPostPath path, final ChildBody body,
- final Map<String, String> queryParameters) {
+ private @NonNull RestconfFuture<CreateResourceResult> dataCreatePOST(final ServerRequest request,
+ final DatabindPath.Data path, final ChildBody body) {
final Insert insert;
try {
- insert = Insert.ofQueryParameters(path.databind(), queryParameters);
+ insert = Insert.of(path.databind(), request.queryParameters());
} catch (IllegalArgumentException e) {
return RestconfFuture.failed(new RestconfDocumentedException(e.getMessage(),
ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e));
return ret;
}
- private @NonNull RestconfFuture<InvokeOperation> dataInvokePOST(final OperationPath.Action path,
- final OperationInputBody body) {
- final var inference = path.inference();
+ private @NonNull RestconfFuture<InvokeResult> dataInvokePOST(final @NonNull Action path,
+ final @NonNull OperationInputBody body) {
final ContainerNode input;
try {
- input = body.toContainerNode(new OperationsPostPath(databind, inference));
+ input = body.toContainerNode(path);
} catch (IOException e) {
LOG.debug("Error reading input", e);
return RestconfFuture.failed(new RestconfDocumentedException("Error parsing input: " + e.getMessage(),
return RestconfFuture.failed(new RestconfDocumentedException("DOMActionService is missing."));
}
- final var future = dataInvokePOST(actionService, path, input);
- return future.transform(result -> result.getOutput()
- .flatMap(output -> output.isEmpty() ? Optional.empty()
- : Optional.of(new InvokeOperation(new NormalizedNodePayload(inference, output))))
- .orElse(InvokeOperation.EMPTY));
+ return dataInvokePOST(actionService, path, input)
+ .transform(result -> outputToInvokeResult(path, result.getOutput().orElse(null)));
}
/**
* @return {@link DOMActionResult}
*/
private static RestconfFuture<DOMActionResult> dataInvokePOST(final DOMActionService actionService,
- final OperationPath.Action path, final @NonNull ContainerNode input) {
+ final Action path, final @NonNull ContainerNode input) {
final var ret = new SettableRestconfFuture<DOMActionResult>();
Futures.addCallback(actionService.invokeAction(