import org.opendaylight.restconf.server.api.PatchBody;
import org.opendaylight.restconf.server.api.ResourceBody;
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.ApiPathNormalizer.Path.Action;
+import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Data;
+import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Rpc;
import org.opendaylight.restconf.server.spi.OperationInput;
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;
// 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);
public @NonNull RestconfFuture<DataPutResult> dataPUT(final ApiPath apiPath, final ResourceBody body,
final Map<String, String> queryParameters) {
- final DataPath path;
+ final Data path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
} catch (RestconfDocumentedException e) {
final NormalizedNode data, final @Nullable Insert insert) {
final ListenableFuture<? extends CommitInfo> future;
if (insert != null) {
- final var parentPath = path.coerceParent();
- checkListAndOrderedType(parentPath);
- future = insertAndCommitPost(path, data, insert, parentPath);
+ checkListAndOrderedType(path);
+ future = insertAndCommitPost(path, data, insert);
} else {
future = createAndCommit(prepareWriteExecution(), path, data);
}
}
private ListenableFuture<? extends CommitInfo> insertAndCommitPost(final YangInstanceIdentifier path,
- final NormalizedNode data, final @NonNull Insert insert, final YangInstanceIdentifier parent) {
- final var grandParent = parent.coerceParent();
+ final NormalizedNode data, final @NonNull Insert insert) {
final var tx = prepareWriteExecution();
return switch (insert.insert()) {
case FIRST -> {
- final var readData = tx.readList(grandParent);
+ final var readData = tx.readList(path);
if (readData == null || readData.isEmpty()) {
tx.replace(path, data);
} else {
- checkItemDoesNotExists(exists(path), path);
- tx.remove(grandParent);
+ checkListDataDoesNotExist(path, data);
+ tx.remove(path);
tx.replace(path, data);
- tx.replace(grandParent, readData);
+ tx.replace(path, readData);
}
yield tx.commit();
}
case LAST -> createAndCommit(tx, path, data);
case BEFORE -> {
- final var readData = tx.readList(grandParent);
+ final var readData = tx.readList(path);
if (readData == null || readData.isEmpty()) {
tx.replace(path, data);
} else {
- checkItemDoesNotExists(exists(path), path);
- insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, grandParent, true);
+ checkListDataDoesNotExist(path, data);
+ insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, true);
}
yield tx.commit();
}
case AFTER -> {
- final var readData = tx.readList(grandParent);
+ final var readData = tx.readList(path);
if (readData == null || readData.isEmpty()) {
tx.replace(path, data);
} else {
- checkItemDoesNotExists(exists(path), path);
- insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, grandParent, false);
+ checkListDataDoesNotExist(path, data);
+ insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, false);
}
yield tx.commit();
}
* @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) {
}
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) {
private void insertWithPointPost(final RestconfTransaction tx, final YangInstanceIdentifier path,
final NormalizedNode data, final PathArgument pointArg, final NormalizedNodeContainer<?> readList,
- final YangInstanceIdentifier grandParentPath, final boolean before) {
- tx.remove(grandParentPath);
+ final boolean before) {
+ tx.remove(path);
int lastItemPosition = 0;
for (var nodeChild : readList.body()) {
}
int lastInsertedPosition = 0;
- final var emptySubtree = fromInstanceId(modelContext(), grandParentPath);
- tx.merge(YangInstanceIdentifier.of(emptySubtree.name()), emptySubtree);
for (var nodeChild : readList.body()) {
if (lastInsertedPosition == lastItemPosition) {
tx.replace(path, data);
}
- tx.replace(grandParentPath.node(nodeChild.name()), nodeChild);
+ tx.replace(path.node(nodeChild.name()), nodeChild);
lastInsertedPosition++;
}
return tx.commit();
}
+ /**
+ * Check if child items do NOT already exists in List at specified {@code path}.
+ *
+ * @param data Data to be checked
+ * @param path Path to be checked
+ * @throws RestconfDocumentedException if data already exists.
+ */
+ private void checkListDataDoesNotExist(final YangInstanceIdentifier path, final NormalizedNode data) {
+ if (data instanceof NormalizedNodeContainer<?> dataNode) {
+ for (final var node : dataNode.body()) {
+ checkItemDoesNotExists(exists(path.node(node.name())), path.node(node.name()));
+ }
+ } else {
+ throw new RestconfDocumentedException("Unexpected node type: " + data.getClass().getName());
+ }
+ }
+
/**
* Check if items do NOT already exists at specified {@code path}.
*
*/
@SuppressWarnings("checkstyle:abbreviationAsWordInName")
public final @NonNull RestconfFuture<Empty> dataDELETE(final ApiPath apiPath) {
- final DataPath path;
+ final Data path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
} catch (RestconfDocumentedException e) {
public final @NonNull RestconfFuture<DataGetResult> dataGET(final ApiPath apiPath,
final DataGetParams params) {
- final DataPath path;
+ final Data path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
} catch (RestconfDocumentedException e) {
return dataGET(path, params);
}
- abstract @NonNull RestconfFuture<DataGetResult> dataGET(DataPath path, DataGetParams params);
+ abstract @NonNull RestconfFuture<DataGetResult> dataGET(Data path, DataGetParams params);
static final @NonNull RestconfFuture<DataGetResult> completeDataGET(final Inference inference,
final QueryParameters queryParams, final @Nullable NormalizedNode node,
static final NormalizedNode mergeConfigAndSTateDataIfNeeded(final NormalizedNode stateDataNode,
final NormalizedNode configDataNode) {
- // if no data exists
- if (stateDataNode == null && configDataNode == null) {
- return null;
- }
-
- // return config data
if (stateDataNode == null) {
+ // No state, return config
return configDataNode;
}
-
- // return state data
if (configDataNode == null) {
+ // No config, return state
return stateDataNode;
}
-
- // merge data from config and state
+ // merge config and state
return mergeStateAndConfigData(stateDataNode, configDataNode);
}
.collect(ImmutableSet.toImmutableSet());
if (!rpcNames.isEmpty()) {
final var namespace = entry.getKey();
- table.computeIfAbsent(namespace.getNamespace(), ignored -> new HashMap<>())
- .put(namespace.getRevision().orElse(null), rpcNames);
+ table.computeIfAbsent(namespace.namespace(), ignored -> new HashMap<>())
+ .put(namespace.revision(), rpcNames);
}
}
entry.getValue().entrySet().stream()
.sorted(Comparator.comparing(Entry::getKey, (first, second) -> Revision.compare(second, first)))
.findFirst()
- .ifPresent(row -> rpcs.putAll(QNameModule.create(entry.getKey(), row.getKey()), row.getValue()));
+ .ifPresent(row -> rpcs.putAll(QNameModule.of(entry.getKey(), row.getKey()), row.getValue()));
}
return RestconfFuture.of(new OperationsGetResult.Container(modelContext, rpcs.build()));
}
public @NonNull RestconfFuture<OperationsPostResult> operationsPOST(final URI restconfURI, final ApiPath apiPath,
final OperationInputBody body) {
- final OperationPath.Rpc path;
+ final Rpc path;
try {
path = pathNormalizer.normalizeRpcPath(apiPath);
} catch (RestconfDocumentedException e) {
} 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);
}
}
- if (path instanceof OperationPath.Action actionPath) {
+ if (path instanceof Action actionPath) {
try (var inputBody = body.toOperationInput()) {
return dataInvokePOST(actionPath, inputBody);
}
return ret;
}
- private @NonNull RestconfFuture<InvokeOperation> dataInvokePOST(final OperationPath.Action path,
- final OperationInputBody body) {
+ private @NonNull RestconfFuture<InvokeOperation> dataInvokePOST(final Action path, final OperationInputBody body) {
final var inference = path.inference();
final ContainerNode input;
try {
* @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(