*/
package org.opendaylight.restconf.server.api;
-import static com.google.common.base.Verify.verify;
-
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
throws IOException;
static final YangInstanceIdentifier parsePatchTarget(final DatabindPath.@NonNull Data path, final String target) {
- final var urlPath = path.instance();
- if (target.equals("/")) {
- verify(!urlPath.isEmpty(),
- "target resource of URI must not be a datastore resource when target is '/'");
- return urlPath;
- }
-
- // FIXME: NETCONF-1157: these two operations should really be a single ApiPathNormalizer step -- but for that
- // we need to switch to ApiPath forms
- final var pathNormalizer = new ApiPathNormalizer(path.databind());
- final String targetUrl;
- if (urlPath.isEmpty()) {
- targetUrl = target.startsWith("/") ? target.substring(1) : target;
- } else {
- targetUrl = pathNormalizer.canonicalize(urlPath).toString() + target;
+ // As per: https://www.rfc-editor.org/rfc/rfc8072#page-18:
+ //
+ // "Identifies the target data node for the edit
+ // operation. If the target has the value '/', then
+ // the target data node is the target resource.
+ // The target node MUST identify a data resource,
+ // not the datastore resource.";
+ //
+ final ApiPath targetPath;
+ try {
+ targetPath = ApiPath.parse(target.startsWith("/") ? target.substring(1) : target);
+ } catch (ParseException e) {
+ throw new RestconfDocumentedException("Failed to parse edit target '" + target + "'",
+ ErrorType.RPC, ErrorTag.MALFORMED_MESSAGE, e);
}
+ final YangInstanceIdentifier result;
try {
- return pathNormalizer.normalizeDataPath(ApiPath.parse(targetUrl)).instance();
- } catch (ParseException | RestconfDocumentedException e) {
- throw new RestconfDocumentedException("Failed to parse target " + target,
+ result = ApiPathNormalizer.normalizeSubResource(path, targetPath).instance();
+ } catch (RestconfDocumentedException e) {
+ throw new RestconfDocumentedException("Invalid edit target '" + targetPath + "'",
ErrorType.RPC, ErrorTag.MALFORMED_MESSAGE, e);
}
+ if (result.isEmpty()) {
+ throw new RestconfDocumentedException("Target node resource must not be a datastore resource",
+ ErrorType.RPC, ErrorTag.MALFORMED_MESSAGE);
+ }
+ return result;
}
/**
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
}
+ public static @NonNull Data normalizeSubResource(final Data resource, final ApiPath subResource) {
+ // If subResource is empty just return the resource
+ final var urlPath = resource.instance();
+ if (subResource.steps().isEmpty()) {
+ return resource;
+ }
+ final var normalizer = new ApiPathNormalizer(resource.databind());
+ if (urlPath.isEmpty()) {
+ // URL indicates the datastore resource, let's just normalize targetPath
+ return normalizer.normalizeDataPath(subResource);
+ }
+
+ // FIXME: We are re-parsing the concatenation. We should provide enough context for the bottom half of
+ // normalizePath() logic instead
+ final String targetUrl = normalizer.canonicalize(urlPath).toString() + "/" + subResource.toString();
+ try {
+ return normalizer.normalizeDataPath(ApiPath.parse(targetUrl));
+ } catch (ParseException e) {
+ throw new RestconfDocumentedException("Failed to parse target " + targetUrl,
+ ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, e);
+ }
+ }
+
@Override
public PathArgument normalizePoint(final ApiPath value) {
final var path = normalizePath(value);