import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.fromInstanceId;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import org.opendaylight.mdsal.dom.api.DOMRpcResult;
import org.opendaylight.mdsal.dom.api.DOMRpcService;
import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService.YangTextSourceExtension;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
-import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
import org.opendaylight.restconf.api.ApiPath;
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.databind.ChildBody;
-import org.opendaylight.restconf.nb.rfc8040.databind.DataPostBody;
-import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
-import org.opendaylight.restconf.nb.rfc8040.databind.PatchBody;
-import org.opendaylight.restconf.nb.rfc8040.databind.ResourceBody;
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.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.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.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.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContext;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
+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.repo.api.SchemaSourceRepresentation;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
import org.slf4j.Logger;
private static final Logger LOG = LoggerFactory.getLogger(RestconfStrategy.class);
private static final @NonNull DataPutResult PUT_CREATED = new DataPutResult(true);
private static final @NonNull DataPutResult PUT_REPLACED = new DataPutResult(false);
+ private static final @NonNull DataPatchResult PATCH_EMPTY = new DataPatchResult();
private final @NonNull ImmutableMap<QName, RpcImplementation> localRpcs;
private final @NonNull ApiPathNormalizer pathNormalizer;
private final @NonNull DatabindContext databind;
- private final DOMYangTextSourceProvider sourceProvider;
+ private final YangTextSourceExtension sourceProvider;
private final DOMMountPointService mountPointService;
private final DOMActionService actionService;
private final DOMRpcService rpcService;
RestconfStrategy(final DatabindContext databind, final ImmutableMap<QName, RpcImplementation> localRpcs,
final @Nullable DOMRpcService rpcService, final @Nullable DOMActionService actionService,
- final DOMYangTextSourceProvider sourceProvider, final @Nullable DOMMountPointService mountPointService) {
+ final YangTextSourceExtension sourceProvider, final @Nullable DOMMountPointService mountPointService) {
this.databind = requireNonNull(databind);
this.localRpcs = requireNonNull(localRpcs);
this.rpcService = rpcService;
final var rpcService = mountPoint.getService(DOMRpcService.class).orElse(null);
final var actionService = mountPoint.getService(DOMActionService.class).orElse(null);
final var sourceProvider = mountPoint.getService(DOMSchemaService.class)
- .flatMap(schema -> Optional.ofNullable(schema.getExtensions().getInstance(DOMYangTextSourceProvider.class)))
+ .flatMap(schema -> Optional.ofNullable(schema.extension(YangTextSourceExtension.class)))
.orElse(null);
final var netconfService = mountPoint.getService(NetconfDataTreeService.class);
abstract ListenableFuture<Boolean> exists(YangInstanceIdentifier path);
@VisibleForTesting
- final @NonNull RestconfFuture<Empty> merge(final YangInstanceIdentifier path, final NormalizedNode data) {
- final var ret = new SettableRestconfFuture<Empty>();
+ final @NonNull RestconfFuture<DataPatchResult> merge(final YangInstanceIdentifier path, final NormalizedNode data) {
+ final var ret = new SettableRestconfFuture<DataPatchResult>();
merge(ret, requireNonNull(path), requireNonNull(data));
return ret;
}
- private void merge(final @NonNull SettableRestconfFuture<Empty> future, final @NonNull YangInstanceIdentifier path,
- final @NonNull NormalizedNode data) {
+ private void merge(final @NonNull SettableRestconfFuture<DataPatchResult> future,
+ final @NonNull YangInstanceIdentifier path, final @NonNull NormalizedNode data) {
final var tx = prepareWriteExecution();
// FIXME: this method should be further specialized to eliminate this call -- it is only needed for MD-SAL
tx.ensureParentsByMerge(path);
Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
@Override
public void onSuccess(final CommitInfo result) {
- future.set(Empty.value());
+ // TODO: extract details once CommitInfo can communicate them
+ future.set(PATCH_EMPTY);
}
@Override
}
int lastInsertedPosition = 0;
- final var emptySubtree = ImmutableNodes.fromInstanceId(modelContext(), path.getParent());
+ final var emptySubtree = fromInstanceId(modelContext(), path.getParent());
tx.merge(YangInstanceIdentifier.of(emptySubtree.name()), emptySubtree);
for (var nodeChild : readList.body()) {
if (lastInsertedPosition == lastItemPosition) {
tx.replace(childPath, nodeChild);
lastInsertedPosition++;
}
+
+ // In case we are inserting after last element
+ if (!before) {
+ if (lastInsertedPosition == lastItemPosition) {
+ tx.replace(path, data);
+ }
+ }
}
private static ListenableFuture<? extends CommitInfo> replaceAndCommit(final RestconfTransaction tx,
* @return A {@link RestconfFuture}
* @throws NullPointerException if any argument is {@code null}
*/
- public final @NonNull RestconfFuture<Empty> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
+ public final @NonNull RestconfFuture<DataPatchResult> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
final DataPath path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
return merge(path.instance(), data);
}
- public final @NonNull RestconfFuture<PatchStatusContext> dataPATCH(final ApiPath apiPath, final PatchBody body) {
+ public final @NonNull RestconfFuture<DataYangPatchResult> dataPATCH(final ApiPath apiPath, final PatchBody body) {
final DataPath path;
try {
path = pathNormalizer.normalizeDataPath(apiPath);
* @param patch Patch context to be processed
* @return {@link PatchStatusContext}
*/
- public final @NonNull RestconfFuture<PatchStatusContext> patchData(final PatchContext patch) {
+ public final @NonNull RestconfFuture<DataYangPatchResult> patchData(final PatchContext patch) {
final var editCollection = new ArrayList<PatchStatusEntity>();
final var tx = prepareWriteExecution();
}
}
- final var ret = new SettableRestconfFuture<PatchStatusContext>();
+ final var ret = new SettableRestconfFuture<DataYangPatchResult>();
// We have errors
if (!noError) {
tx.cancel();
- ret.set(new PatchStatusContext(modelContext(), patch.patchId(), List.copyOf(editCollection), false, null));
+ ret.set(new DataYangPatchResult(
+ new PatchStatusContext(modelContext(), patch.patchId(), List.copyOf(editCollection), false, null)));
return ret;
}
Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
@Override
public void onSuccess(final CommitInfo result) {
- ret.set(new PatchStatusContext(modelContext(), patch.patchId(), List.copyOf(editCollection), true,
- null));
+ ret.set(new DataYangPatchResult(
+ new PatchStatusContext(modelContext(), 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 PatchStatusContext(modelContext(), patch.patchId(), List.copyOf(editCollection), false,
- TransactionUtil.decodeException(cause, "PATCH", null, modelContext()).getErrors()));
+ ret.set(new DataYangPatchResult(
+ new PatchStatusContext(modelContext(), patch.patchId(), List.copyOf(editCollection), false,
+ TransactionUtil.decodeException(cause, "PATCH", null, modelContext()).getErrors())));
}
}, MoreExecutors.directExecutor());
}
int lastInsertedPosition = 0;
- final var emptySubtree = ImmutableNodes.fromInstanceId(modelContext(), grandParentPath);
+ final var emptySubtree = fromInstanceId(modelContext(), grandParentPath);
tx.merge(YangInstanceIdentifier.of(emptySubtree.name()), emptySubtree);
for (var nodeChild : readList.body()) {
if (lastInsertedPosition == lastItemPosition) {
tx.replace(grandParentPath.node(nodeChild.name()), nodeChild);
lastInsertedPosition++;
}
+
+ // In case we are inserting after last element
+ if (!before) {
+ if (lastInsertedPosition == lastItemPosition) {
+ tx.replace(path, data);
+ }
+ }
}
private static ListenableFuture<? extends CommitInfo> createAndCommit(final RestconfTransaction tx,
* @return {@link NormalizedNode}
*/
// FIXME: NETCONF-1155: this method should asynchronous
- public final @Nullable NormalizedNode readData(final @NonNull ContentParam content,
+ @VisibleForTesting
+ final @Nullable NormalizedNode readData(final @NonNull ContentParam content,
final @NonNull YangInstanceIdentifier path, final WithDefaultsParam defaultsMode) {
return switch (content) {
case ALL -> {
// FIXME: we have this readily available in InstanceIdentifierContext
final var ctxNode = databind.schemaTree().findChild(path).orElseThrow();
if (readData instanceof ContainerNode container) {
- final var builder = Builders.containerBuilder().withNodeIdentifier(container.name());
+ final var builder = ImmutableNodes.newContainerBuilder().withNodeIdentifier(container.name());
buildCont(builder, container.body(), ctxNode, trim);
return builder.build();
} else if (readData instanceof MapEntryNode mapEntry) {
throw new IllegalStateException("Input " + mapEntry + " does not match " + ctxNode);
}
- final var builder = Builders.mapEntryBuilder().withNodeIdentifier(mapEntry.name());
+ final var builder = ImmutableNodes.newMapEntryBuilder().withNodeIdentifier(mapEntry.name());
buildMapEntryBuilder(builder, mapEntry.body(), ctxNode, trim, listSchema.getKeyDefinition());
return builder.build();
} else {
private static void appendContainer(final DataContainerNodeBuilder<?, ?> builder, final ContainerNode container,
final DataSchemaContext ctxNode, final boolean trim) {
- final var childBuilder = Builders.containerBuilder().withNodeIdentifier(container.name());
+ final var childBuilder = ImmutableNodes.newContainerBuilder().withNodeIdentifier(container.name());
buildCont(childBuilder, container.body(), ctxNode, trim);
builder.withChild(childBuilder.build());
}
}
final var childBuilder = switch (map.ordering()) {
- case SYSTEM -> Builders.mapBuilder();
- case USER -> Builders.orderedMapBuilder();
+ case SYSTEM -> ImmutableNodes.newSystemMapBuilder();
+ case USER -> ImmutableNodes.newUserMapBuilder();
};
buildList(childBuilder.withNodeIdentifier(map.name()), map.body(), childCtx, trim,
listSchema.getKeyDefinition());
final List<@NonNull QName> keys) {
for (var entry : entries) {
final var childCtx = getChildContext(ctxNode, entry);
- final var mapEntryBuilder = Builders.mapEntryBuilder().withNodeIdentifier(entry.name());
+ final var mapEntryBuilder = ImmutableNodes.newMapEntryBuilder().withNodeIdentifier(entry.name());
buildMapEntryBuilder(mapEntryBuilder, entry.body(), childCtx, trim, keys);
builder.withChild(mapEntryBuilder.build());
}
*/
private static @NonNull NormalizedNode prepareRpcData(final @NonNull NormalizedNode configDataNode,
final @NonNull NormalizedNode stateDataNode) {
- final var mapEntryBuilder = Builders.mapEntryBuilder()
+ final var mapEntryBuilder = ImmutableNodes.newMapEntryBuilder()
.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.name());
// MAP CONFIG DATA
// MAP STATE DATA
mapRpcDataNode(stateDataNode, mapEntryBuilder);
- return Builders.mapBuilder()
+ return ImmutableNodes.newSystemMapBuilder()
.withNodeIdentifier(NodeIdentifier.create(configDataNode.name().getNodeType()))
.addChild(mapEntryBuilder.build())
.build();
private static @NonNull NormalizedNode prepareData(final @NonNull NormalizedNode configDataNode,
final @NonNull NormalizedNode stateDataNode) {
if (configDataNode instanceof UserMapNode configMap) {
- final var builder = Builders.orderedMapBuilder().withNodeIdentifier(configMap.name());
+ final var builder = ImmutableNodes.newUserMapBuilder().withNodeIdentifier(configMap.name());
mapValueToBuilder(configMap.body(), ((UserMapNode) stateDataNode).body(), builder);
return builder.build();
} else if (configDataNode instanceof SystemMapNode configMap) {
- final var builder = Builders.mapBuilder().withNodeIdentifier(configMap.name());
+ final var builder = ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(configMap.name());
mapValueToBuilder(configMap.body(), ((SystemMapNode) stateDataNode).body(), builder);
return builder.build();
} else if (configDataNode instanceof MapEntryNode configEntry) {
- final var builder = Builders.mapEntryBuilder().withNodeIdentifier(configEntry.name());
+ final var builder = ImmutableNodes.newMapEntryBuilder().withNodeIdentifier(configEntry.name());
mapValueToBuilder(configEntry.body(), ((MapEntryNode) stateDataNode).body(), builder);
return builder.build();
} else if (configDataNode instanceof ContainerNode configContaienr) {
- final var builder = Builders.containerBuilder().withNodeIdentifier(configContaienr.name());
+ final var builder = ImmutableNodes.newContainerBuilder().withNodeIdentifier(configContaienr.name());
mapValueToBuilder(configContaienr.body(), ((ContainerNode) stateDataNode).body(), builder);
return builder.build();
} else if (configDataNode instanceof ChoiceNode configChoice) {
- final var builder = Builders.choiceBuilder().withNodeIdentifier(configChoice.name());
+ final var builder = ImmutableNodes.newChoiceBuilder().withNodeIdentifier(configChoice.name());
mapValueToBuilder(configChoice.body(), ((ChoiceNode) stateDataNode).body(), builder);
return builder.build();
} else if (configDataNode instanceof LeafNode configLeaf) {
return configLeaf;
} else if (configDataNode instanceof UserLeafSetNode) {
final var configLeafSet = (UserLeafSetNode<Object>) configDataNode;
- final var builder = Builders.<Object>orderedLeafSetBuilder().withNodeIdentifier(configLeafSet.name());
+ final var builder = ImmutableNodes.<Object>newUserLeafSetBuilder().withNodeIdentifier(configLeafSet.name());
mapValueToBuilder(configLeafSet.body(), ((UserLeafSetNode<Object>) stateDataNode).body(), builder);
return builder.build();
} else if (configDataNode instanceof SystemLeafSetNode) {
final var configLeafSet = (SystemLeafSetNode<Object>) configDataNode;
- final var builder = Builders.<Object>leafSetBuilder().withNodeIdentifier(configLeafSet.name());
+ final var builder = ImmutableNodes.<Object>newSystemLeafSetBuilder()
+ .withNodeIdentifier(configLeafSet.name());
mapValueToBuilder(configLeafSet.body(), ((SystemLeafSetNode<Object>) stateDataNode).body(), builder);
return builder.build();
} else if (configDataNode instanceof LeafSetEntryNode<?> configEntry) {
// config trumps oper
return configEntry;
} else if (configDataNode instanceof UnkeyedListNode configList) {
- final var builder = Builders.unkeyedListBuilder().withNodeIdentifier(configList.name());
+ final var builder = ImmutableNodes.newUnkeyedListBuilder().withNodeIdentifier(configList.name());
mapValueToBuilder(configList.body(), ((UnkeyedListNode) stateDataNode).body(), builder);
return builder.build();
} else if (configDataNode instanceof UnkeyedListEntryNode configEntry) {
- final var builder = Builders.unkeyedListEntryBuilder().withNodeIdentifier(configEntry.name());
+ final var builder = ImmutableNodes.newUnkeyedListEntryBuilder().withNodeIdentifier(configEntry.name());
mapValueToBuilder(configEntry.body(), ((UnkeyedListEntryNode) stateDataNode).body(), builder);
return builder.build();
} else {
return RestconfFuture.failed(e);
}
- return RestconfFuture.of(
- new OperationsGetResult.Leaf(rpc.inference().getEffectiveModelContext(), rpc.rpc().argument()));
+ return RestconfFuture.of(new OperationsGetResult.Leaf(rpc.inference().modelContext(), rpc.rpc().argument()));
}
public @NonNull RestconfFuture<OperationsPostResult> operationsPOST(final URI restconfURI, final ApiPath apiPath,
}
public @NonNull RestconfFuture<CharSource> resolveSource(final SourceIdentifier source,
- final Class<? extends SchemaSourceRepresentation> representation) {
+ final Class<? extends SourceRepresentation> representation) {
final var src = requireNonNull(source);
- if (YangTextSchemaSource.class.isAssignableFrom(representation)) {
+ if (YangTextSource.class.isAssignableFrom(representation)) {
if (sourceProvider != null) {
final var ret = new SettableRestconfFuture<CharSource>();
- Futures.addCallback(sourceProvider.getSource(src), new FutureCallback<YangTextSchemaSource>() {
+ Futures.addCallback(sourceProvider.getYangTexttSource(src), new FutureCallback<>() {
@Override
- public void onSuccess(final YangTextSchemaSource result) {
+ public void onSuccess(final YangTextSource result) {
ret.set(result);
}
}
return exportSource(modelContext(), src, YangCharSource::new, YangCharSource::new);
}
- if (YinTextSchemaSource.class.isAssignableFrom(representation)) {
+ if (YinTextSource.class.isAssignableFrom(representation)) {
return exportSource(modelContext(), src, YinCharSource.OfModule::new, YinCharSource.OfSubmodule::new);
}
return RestconfFuture.failed(new RestconfDocumentedException(