This is the next step in RDE eradication.
JIRA: NETCONF-1188
Change-Id: I9123b37849636e7d38167b280265d0d900565a96
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
Futures.addCallback(future, new FutureCallback<CommitInfo>() {
@Override
public void onSuccess(final CommitInfo result) {
- ret.set(new CreateResourceResult(new ApiPathCanonizer(databind).dataToApiPath(
- data instanceof MapNode mapData && !mapData.isEmpty()
- ? path.node(mapData.body().iterator().next().name()) : path)));
+ final ApiPath apiPath;
+ try {
+ apiPath = new ApiPathCanonizer(databind).dataToApiPath(
+ data instanceof MapNode mapData && !mapData.isEmpty()
+ ? path.node(mapData.body().iterator().next().name()) : path);
+ } catch (ServerException e) {
+ // This should never happen
+ ret.setFailure(e.toLegacy());
+ return;
+ }
+ ret.set(new CreateResourceResult(apiPath));
}
@Override
import javax.inject.Inject;
import javax.inject.Singleton;
import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.errors.RestconfFuture;
+import org.opendaylight.restconf.server.api.ServerException;
import org.opendaylight.restconf.server.spi.ApiPathCanonizer;
import org.opendaylight.restconf.server.spi.OperationInput;
import org.opendaylight.restconf.server.spi.RestconfStream;
ErrorType.APPLICATION, ErrorTag.INVALID_VALUE));
}
+ final ApiPath apiPath;
+ try {
+ apiPath = new ApiPathCanonizer(input.path().databind()).dataToApiPath(path);
+ } catch (ServerException e) {
+ return RestconfFuture.failed(e.toLegacy());
+ }
+
return streamRegistry.createStream(restconfURI, new DeviceNotificationSource(mountPointService, path),
- "All YANG notifications occuring on mount point /"
- + new ApiPathCanonizer(input.path().databind()).dataToApiPath(path).toString())
+ "All YANG notifications occuring on mount point /" + apiPath.toString())
.transform(stream -> ImmutableNodes.newContainerBuilder()
.withNodeIdentifier(new NodeIdentifier(SubscribeDeviceNotificationOutput.QNAME))
.withChild(ImmutableNodes.leafNode(DEVICE_NOTIFICATION_STREAM_NAME_NODEID, stream.name()))
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
import org.opendaylight.mdsal.dom.api.DOMDataBroker.DataTreeChangeExtension;
+import org.opendaylight.restconf.api.ApiPath;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.common.errors.RestconfFuture;
+import org.opendaylight.restconf.server.api.ServerException;
import org.opendaylight.restconf.server.spi.ApiPathCanonizer;
import org.opendaylight.restconf.server.spi.DatabindProvider;
import org.opendaylight.restconf.server.spi.OperationInput;
new RestconfDocumentedException("missing path", ErrorType.APPLICATION, ErrorTag.MISSING_ELEMENT));
}
+ final ApiPath apiPath;
+ try {
+ apiPath = new ApiPathCanonizer(input.path().databind()).dataToApiPath(path);
+ } catch (ServerException e) {
+ return RestconfFuture.failed(e.toLegacy());
+ }
+
return streamRegistry.createStream(restconfURI,
new DataTreeChangeSource(databindProvider, changeService, datastore, path),
- "Events occuring in " + datastore + " datastore under /"
- + new ApiPathCanonizer(input.path().databind()).dataToApiPath(path).toString())
+ "Events occuring in " + datastore + " datastore under /" + apiPath.toString())
.transform(stream -> ImmutableNodes.newContainerBuilder()
.withNodeIdentifier(OUTPUT_NODEID)
.withChild(ImmutableNodes.leafNode(STREAM_NAME_NODEID, stream.name()))
import org.opendaylight.restconf.api.ApiPath.ApiIdentifier;
import org.opendaylight.restconf.api.ApiPath.ListInstance;
import org.opendaylight.restconf.api.ApiPath.Step;
-import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
import org.opendaylight.restconf.server.api.DatabindContext;
+import org.opendaylight.restconf.server.api.ServerException;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.QName;
*
* @param path {@link YangInstanceIdentifier} to canonicalize
* @return An {@link ApiPath}
+ * @throws ServerException if an error occurs
*/
- public @NonNull ApiPath dataToApiPath(final YangInstanceIdentifier path) {
+ public @NonNull ApiPath dataToApiPath(final YangInstanceIdentifier path) throws ServerException {
final var it = path.getPathArguments().iterator();
if (!it.hasNext()) {
return ApiPath.empty();
final var childContext = context instanceof Composite composite ? composite.enterChild(stack, arg) : null;
if (childContext == null) {
- throw new RestconfDocumentedException(
- "Invalid input '%s': schema for argument '%s' (after '%s') not found".formatted(path, arg,
- ApiPath.of(builder.build())), ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
+ throw new ServerException(ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT,
+ "Invalid input '%s': schema for argument '%s' (after '%s') not found", path, arg,
+ ApiPath.of(builder.build()));
}
context = childContext;
}
private @NonNull Step argToStep(final PathArgument arg, final QNameModule prevNamespace,
- final SchemaInferenceStack stack, final DataSchemaContext context) {
+ final SchemaInferenceStack stack, final DataSchemaContext context) throws ServerException {
// append namespace before every node which is defined in other module than its parent
// condition is satisfied also for the first path argument
final var nodeType = arg.getNodeType();
final var schema = context.dataSchemaNode();
if (arg instanceof NodeWithValue<?> withValue) {
if (!(schema instanceof LeafListSchemaNode leafList)) {
- throw new RestconfDocumentedException(
- "Argument '%s' does not map to a leaf-list, but %s".formatted(arg, schema),
- ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
+ throw new ServerException(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+ "Argument '%s' does not map to a leaf-list, but %s", arg, schema);
}
return ListInstance.of(module, identifier, valueToString(stack, leafList, withValue.getValue()));
}
// A NodeIdentifierWithPredicates adresses a MapEntryNode and maps to a ListInstance with one or more values:
// 1) schema has to be a ListSchemaNode
if (!(schema instanceof ListSchemaNode list)) {
- throw new RestconfDocumentedException(
- "Argument '%s' does not map to a list, but %s".formatted(arg, schema),
- ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
+ throw new ServerException(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+ "Argument '%s' does not map to a list, but %s", arg, schema);
}
// 2) the key definition must be non-empty
final var keyDef = list.getKeyDefinition();
final var size = keyDef.size();
if (size == 0) {
- throw new RestconfDocumentedException(
- "Argument '%s' maps a list without any keys %s".formatted(arg, schema),
- ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
+ throw new ServerException(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+ "Argument '%s' maps a list without any keys %s", arg, schema);
}
// 3) the number of predicates has to match the number of keys
if (size != withPredicates.size()) {
- throw new RestconfDocumentedException(
- "Argument '%s' does not match required keys %s".formatted(arg, keyDef),
- ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
+ throw new ServerException(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+ "Argument '%s' does not match required keys %s", arg, keyDef);
}
// ListSchemaNode implies the context is a composite, verify that instead of an unexplained cast when we look
for (var key : keyDef) {
final var value = withPredicates.getValue(key);
if (value == null) {
- throw new RestconfDocumentedException("Argument '%s' is missing predicate for %s".formatted(arg, key),
- ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
+ throw new ServerException(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+ "Argument '%s' is missing predicate for %s", arg, key);
}
final var tmpStack = stack.copy();
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.opendaylight.restconf.api.ApiPath;
+import org.opendaylight.restconf.api.ErrorMessage;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.common.errors.RestconfError;
import org.opendaylight.restconf.server.api.DatabindContext;
import org.opendaylight.restconf.server.api.DatabindPath.Data;
+import org.opendaylight.restconf.server.api.ServerError;
import org.opendaylight.restconf.server.api.ServerException;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
.node(QName.create("serializer:test", "2016-06-06", "list-one-key"))
.nodeWithKey(QName.create("serializer:test", "2016-06-06", "list-one-key"), Map.of())
.build());
- assertEquals("""
+ assertEquals(new ErrorMessage("""
Argument '(serializer:test?revision=2016-06-06)list-one-key[{}]' does not match required keys \
- [(serializer:test?revision=2016-06-06)name]""", error.getErrorMessage());
- assertEquals(ErrorType.APPLICATION, error.getErrorType());
- assertEquals(ErrorTag.INVALID_VALUE, error.getErrorTag());
+ [(serializer:test?revision=2016-06-06)name]"""), error.message());
+ assertEquals(ErrorType.APPLICATION, error.type());
+ assertEquals(ErrorTag.INVALID_VALUE, error.tag());
}
/**
final var error = assertError(YangInstanceIdentifier.of(
QName.create("serializer:test", "2016-06-06", "contA"),
QName.create("serializer:test", "2016-06-06", "not-existing-leaf")));
- assertEquals("""
+ assertEquals(new ErrorMessage("""
Invalid input '/(serializer:test?revision=2016-06-06)contA/not-existing-leaf': schema for argument \
- '(serializer:test?revision=2016-06-06)not-existing-leaf' (after 'serializer-test:contA') not found""",
- error.getErrorMessage());
- assertEquals(ErrorType.APPLICATION, error.getErrorType());
- assertEquals(ErrorTag.UNKNOWN_ELEMENT, error.getErrorTag());
+ '(serializer:test?revision=2016-06-06)not-existing-leaf' (after 'serializer-test:contA') not found"""),
+ error.message());
+ assertEquals(ErrorType.APPLICATION, error.type());
+ assertEquals(ErrorTag.UNKNOWN_ELEMENT, error.tag());
}
/**
// child should has different namespace
.node(QName.create("serializer:test:included", "2016-06-06", "augmented-leaf"))
.build());
- assertEquals("""
+ assertEquals(new ErrorMessage("""
Invalid input '/(serializer:test:included?revision=2016-06-06)augmented-list/augmented-list[{(\
serializer:test:included?revision=2016-06-06)list-key=100}]/augmented-leaf': schema for argument \
'(serializer:test:included?revision=2016-06-06)augmented-leaf' (after \
- 'serializer-test-included:augmented-list=100') not found""", error.getErrorMessage());
- assertEquals(ErrorType.APPLICATION, error.getErrorType());
- assertEquals(ErrorTag.UNKNOWN_ELEMENT, error.getErrorTag());
+ 'serializer-test-included:augmented-list=100') not found"""), error.message());
+ assertEquals(ErrorType.APPLICATION, error.type());
+ assertEquals(ErrorTag.UNKNOWN_ELEMENT, error.tag());
}
/**
}
private static void assertApiPath(final String expected, final YangInstanceIdentifier path) {
- assertEquals(newApiPath(expected), CANONIZER.dataToApiPath(path));
+ try {
+ assertEquals(newApiPath(expected), CANONIZER.dataToApiPath(path));
+ } catch (ServerException e) {
+ throw new AssertionError(e);
+ }
}
private static YangInstanceIdentifier assertNormalized(final String str) {
}
}
- private static RestconfError assertError(final YangInstanceIdentifier path) {
- final var ex = assertThrows(RestconfDocumentedException.class, () -> CANONIZER.dataToApiPath(path));
- final var errors = ex.getErrors();
- assertEquals(1, errors.size());
- return errors.get(0);
+ private static ServerError assertError(final YangInstanceIdentifier path) {
+ return assertThrows(ServerException.class, () -> CANONIZER.dataToApiPath(path)).error();
}
private static ApiPath newApiPath(final String apiPath) {