2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.server.api;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.text.ParseException;
13 import org.eclipse.jdt.annotation.NonNull;
14 import org.opendaylight.restconf.api.ApiPath;
15 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
16 import org.opendaylight.restconf.common.patch.PatchContext;
17 import org.opendaylight.restconf.server.spi.ApiPathNormalizer;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.patch.rev170222.yang.patch.yang.patch.Edit.Operation;
19 import org.opendaylight.yangtools.yang.common.ErrorTag;
20 import org.opendaylight.yangtools.yang.common.ErrorType;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 public abstract sealed class PatchBody extends AbstractBody permits JsonPatchBody, XmlPatchBody {
27 PatchBody(final InputStream inputStream) {
31 public final @NonNull PatchContext toPatchContext(final DatabindPath.@NonNull Data path) throws IOException {
32 try (var is = acquireStream()) {
33 return toPatchContext(path, is);
37 abstract @NonNull PatchContext toPatchContext(DatabindPath.@NonNull Data path, @NonNull InputStream inputStream)
40 static final YangInstanceIdentifier parsePatchTarget(final DatabindPath.@NonNull Data path, final String target) {
41 // As per: https://www.rfc-editor.org/rfc/rfc8072#page-18:
43 // "Identifies the target data node for the edit
44 // operation. If the target has the value '/', then
45 // the target data node is the target resource.
46 // The target node MUST identify a data resource,
47 // not the datastore resource.";
49 final ApiPath targetPath;
51 targetPath = ApiPath.parse(target.startsWith("/") ? target.substring(1) : target);
52 } catch (ParseException e) {
53 throw new RestconfDocumentedException("Failed to parse edit target '" + target + "'",
54 ErrorType.RPC, ErrorTag.MALFORMED_MESSAGE, e);
57 final YangInstanceIdentifier result;
59 result = ApiPathNormalizer.normalizeSubResource(path, targetPath).instance();
60 } catch (RestconfDocumentedException e) {
61 throw new RestconfDocumentedException("Invalid edit target '" + targetPath + "'",
62 ErrorType.RPC, ErrorTag.MALFORMED_MESSAGE, e);
64 if (result.isEmpty()) {
65 throw new RestconfDocumentedException("Target node resource must not be a datastore resource",
66 ErrorType.RPC, ErrorTag.MALFORMED_MESSAGE);
72 * Not all patch operations support value node. Check if operation requires value or not.
74 * @param operation Patch edit operation
75 * @return true if operation requires value, false otherwise
77 static final boolean requiresValue(final Operation operation) {
78 return switch (operation) {
79 case Create, Insert, Merge, Replace -> true;
80 case Delete, Move, Remove -> false;