import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
+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 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.DataGetParams;
+import org.opendaylight.restconf.server.api.DataPatchPath;
import org.opendaylight.restconf.server.api.DataPostPath;
import org.opendaylight.restconf.server.api.DataPostResult;
import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
// FIXME: this method should only be needed in MdsalRestconfStrategy
abstract ListenableFuture<Boolean> exists(YangInstanceIdentifier path);
- /**
- * Delete data from the configuration datastore. If the data does not exist, this operation will fail, as outlined
- * in <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.7">RFC8040 section 4.7</a>
- *
- * @param path Path to delete
- * @return A {@link RestconfFuture}
- * @throws NullPointerException if {@code path} is {@code null}
- */
- public final @NonNull RestconfFuture<Empty> delete(final YangInstanceIdentifier path) {
- final var ret = new SettableRestconfFuture<Empty>();
- delete(ret, requireNonNull(path));
- return ret;
- }
-
- abstract void delete(@NonNull SettableRestconfFuture<Empty> future, @NonNull YangInstanceIdentifier path);
-
- /**
- * Merge data into the configuration datastore, as outlined in
- * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040 section 4.6.1</a>.
- *
- * @param path Path to merge
- * @param data Data to merge
- * @return A {@link RestconfFuture}
- * @throws NullPointerException if any argument is {@code null}
- */
- public final @NonNull RestconfFuture<Empty> merge(final YangInstanceIdentifier path, final NormalizedNode data) {
+ @VisibleForTesting
+ final @NonNull RestconfFuture<Empty> merge(final YangInstanceIdentifier path, final NormalizedNode data) {
final var ret = new SettableRestconfFuture<Empty>();
merge(ret, requireNonNull(path), requireNonNull(data));
return ret;
};
}
+ /**
+ * Merge data into the configuration datastore, as outlined in
+ * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040 section 4.6.1</a>.
+ *
+ * @param apiPath Path to merge
+ * @param body Data to merge
+ * @return A {@link RestconfFuture}
+ * @throws NullPointerException if any argument is {@code null}
+ */
+ public final @NonNull RestconfFuture<Empty> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
+ final DataPath path;
+ try {
+ path = pathNormalizer.normalizeDataPath(apiPath);
+ } catch (RestconfDocumentedException e) {
+ return RestconfFuture.failed(e);
+ }
+
+ final NormalizedNode data;
+ try {
+ data = body.toNormalizedNode(new DataPutPath(databind, path.inference(), path.instance()));
+ } catch (RestconfDocumentedException e) {
+ return RestconfFuture.failed(e);
+ }
+
+ return merge(path.instance(), data);
+ }
+
+ public final @NonNull RestconfFuture<PatchStatusContext> dataPATCH(final ApiPath apiPath, final PatchBody body) {
+ final DataPath path;
+ try {
+ path = pathNormalizer.normalizeDataPath(apiPath);
+ } catch (RestconfDocumentedException e) {
+ return RestconfFuture.failed(e);
+ }
+
+ final PatchContext patch;
+ try {
+ patch = body.toPatchContext(new DataPatchPath(databind, path.instance()));
+ } catch (IOException e) {
+ LOG.debug("Error parsing YANG Patch input", e);
+ return RestconfFuture.failed(new RestconfDocumentedException("Error parsing input: " + e.getMessage(),
+ ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, e));
+ }
+ return patchData(patch);
+ }
+
/**
* Process edit operations of one {@link PatchContext}.
*
}
}
+ /**
+ * Delete data from the configuration datastore. If the data does not exist, this operation will fail, as outlined
+ * in <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.7">RFC8040 section 4.7</a>
+ *
+ * @param apiPath Path to delete
+ * @return A {@link RestconfFuture}
+ * @throws NullPointerException if {@code apiPath} is {@code null}
+ */
+ @SuppressWarnings("checkstyle:abbreviationAsWordInName")
+ public final @NonNull RestconfFuture<Empty> dataDELETE(final ApiPath apiPath) {
+ final DataPath path;
+ try {
+ path = pathNormalizer.normalizeDataPath(apiPath);
+ } catch (RestconfDocumentedException e) {
+ return RestconfFuture.failed(e);
+ }
+
+ // FIXME: reject empty YangInstanceIdentifier, as datastores may not be deleted
+ final var ret = new SettableRestconfFuture<Empty>();
+ delete(ret, path.instance());
+ return ret;
+ }
+
+ abstract void delete(@NonNull SettableRestconfFuture<Empty> future, @NonNull YangInstanceIdentifier path);
+
public final @NonNull RestconfFuture<NormalizedNodePayload> dataGET(final ApiPath apiPath,
final DataGetParams params) {
final DataPath path;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
-import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.net.URI;
import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.errors.RestconfFuture;
-import org.opendaylight.restconf.common.patch.PatchContext;
import org.opendaylight.restconf.common.patch.PatchStatusContext;
import org.opendaylight.restconf.nb.rfc8040.databind.ChildBody;
import org.opendaylight.restconf.nb.rfc8040.databind.DataPostBody;
import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy.StrategyAndPath;
import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy.StrategyAndTail;
import org.opendaylight.restconf.server.api.DataGetParams;
-import org.opendaylight.restconf.server.api.DataPatchPath;
import org.opendaylight.restconf.server.api.DataPostResult;
import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
-import org.opendaylight.restconf.server.api.DataPutPath;
import org.opendaylight.restconf.server.api.DataPutResult;
import org.opendaylight.restconf.server.api.DatabindContext;
import org.opendaylight.restconf.server.api.ModulesGetResult;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.YangNames;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
@Override
public RestconfFuture<Empty> dataDELETE(final ApiPath identifier) {
- final StrategyAndPath stratAndPath;
+ final StrategyAndTail stratAndTail;
try {
- stratAndPath = localStrategy().resolveStrategyPath(identifier);
+ stratAndTail = localStrategy().resolveStrategy(identifier);
} catch (RestconfDocumentedException e) {
return RestconfFuture.failed(e);
}
- return stratAndPath.strategy().delete(stratAndPath.path().instance());
+ return stratAndTail.strategy().dataDELETE(stratAndTail.tail());
}
@Override
@Override
public RestconfFuture<Empty> dataPATCH(final ResourceBody body) {
- final var strategy = localStrategy();
- return dataPATCH(strategy, new DataPutPath(strategy.databind()), body);
+ return localStrategy().dataPATCH(ApiPath.empty(), body);
}
@Override
public RestconfFuture<Empty> dataPATCH(final ApiPath identifier, final ResourceBody body) {
- final StrategyAndPath strategyAndPath;
- try {
- strategyAndPath = localStrategy().resolveStrategyPath(identifier);
- } catch (RestconfDocumentedException e) {
- return RestconfFuture.failed(e);
- }
- final var strategy = strategyAndPath.strategy();
- final var path = strategyAndPath.path();
- return dataPATCH(strategy, new DataPutPath(strategy.databind(), path.inference(), path.instance()), body);
- }
-
- private static @NonNull RestconfFuture<Empty> dataPATCH(final RestconfStrategy strategy, final DataPutPath path,
- final ResourceBody body) {
- final NormalizedNode data;
+ final StrategyAndTail strategyAndTail;
try {
- data = body.toNormalizedNode(path);
+ strategyAndTail = localStrategy().resolveStrategy(identifier);
} catch (RestconfDocumentedException e) {
return RestconfFuture.failed(e);
}
- return strategy.merge(path.instance(), data);
+ return strategyAndTail.strategy().dataPATCH(strategyAndTail.tail(), body);
}
@Override
public RestconfFuture<PatchStatusContext> dataPATCH(final PatchBody body) {
- final var strategy = localStrategy();
- return dataPATCH(strategy, new DataPatchPath(strategy.databind(), YangInstanceIdentifier.of()), body);
+ return localStrategy().dataPATCH(ApiPath.empty(), body);
}
@Override
public RestconfFuture<PatchStatusContext> dataPATCH(final ApiPath identifier, final PatchBody body) {
- final StrategyAndPath stratAndPath;
+ final StrategyAndTail strategyAndTail;
try {
- stratAndPath = localStrategy().resolveStrategyPath(identifier);
+ strategyAndTail = localStrategy().resolveStrategy(identifier);
} catch (RestconfDocumentedException e) {
return RestconfFuture.failed(e);
}
- final var strategy = stratAndPath.strategy();
- return dataPATCH(strategy, new DataPatchPath(strategy.databind(), stratAndPath.path().instance()), body);
- }
-
- private static @NonNull RestconfFuture<PatchStatusContext> dataPATCH(final RestconfStrategy strategy,
- final DataPatchPath path, final PatchBody body) {
- final PatchContext patch;
- try {
- patch = body.toPatchContext(path);
- } catch (IOException e) {
- LOG.debug("Error parsing YANG Patch input", e);
- return RestconfFuture.failed(new RestconfDocumentedException("Error parsing input: " + e.getMessage(),
- ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, e));
- }
- return strategy.patchData(patch);
+ return strategyAndTail.strategy().dataPATCH(strategyAndTail.tail(), body);
}
@Override
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
+import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.api.query.ContentParam;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.patch.PatchContext;
*/
@Test
public final void testDeleteData() throws Exception {
- final var future = testDeleteDataStrategy().delete(YangInstanceIdentifier.of());
+ final var future = testDeleteDataStrategy().dataDELETE(ApiPath.empty());
assertNotNull(Futures.getDone(future));
}
*/
@Test
public final void testNegativeDeleteData() {
- final var future = testNegativeDeleteDataStrategy().delete(YangInstanceIdentifier.of());
+ final var future = testNegativeDeleteDataStrategy().dataDELETE(ApiPath.empty());
final var ex = assertThrows(ExecutionException.class, () -> Futures.getDone(future)).getCause();
assertThat(ex, instanceOf(RestconfDocumentedException.class));
final var errors = ((RestconfDocumentedException) ex).getErrors();