diff options
author | 2018-10-08 19:11:02 +0530 | |
---|---|---|
committer | 2018-10-09 06:37:21 +0000 | |
commit | bc2c9899ae7d71d0e4a970c146a5121822913ff9 (patch) | |
tree | b966f46fdd784f3cffd74937e92b1cc50d352a60 /restconf-client/provider/src/main | |
parent | da54fc49d3f16d7028a42cd90ad1500973814790 (diff) |
PUT and PATCH operation support
Changes to add a additional root node to the data format req to support PUT and PATCH operations in RestconfApiCallNode
Issue-ID: CCSDK-614
Change-Id: If094810861f8152a2a6d6ee86e9f81c8812b8ad6
Signed-off-by: janani b <janani.b@huawei.com>
Diffstat (limited to 'restconf-client/provider/src/main')
4 files changed, 204 insertions, 34 deletions
diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java index 91ce3338..a76b27d0 100644 --- a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java @@ -20,6 +20,14 @@ package org.onap.ccsdk.sli.plugins.restconfapicall; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonWriter; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; import org.onap.ccsdk.sli.core.sli.SvcLogicContext; import org.onap.ccsdk.sli.core.sli.SvcLogicException; import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; @@ -35,30 +43,39 @@ import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.MdsalSerializerHe import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.SerializerHelper; import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.YangParameters; import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeSerializer; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.Namespace; import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNodeSerializer; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.core.UriBuilder; +import java.io.StringWriter; +import java.io.Writer; import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import static com.google.common.base.Strings.repeat; import static java.lang.String.format; +import static java.lang.String.valueOf; import static org.apache.commons.lang3.StringUtils.join; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.DELETE; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.GET; import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PATCH; -import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.POST; import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PUT; import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.parseParam; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.ATTEMPTS_MSG; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.COLON; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.COMMA; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.COMM_FAIL; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.HEADER; @@ -74,10 +91,16 @@ import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RES_PR import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RETRY_COUNT; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RETRY_FAIL; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.UPDATED_URL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.getUpdatedXmlReq; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.getSchemaCtxFromDir; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.getYangParameters; import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.parseUrl; import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfListenerFactory.instance; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.FORMAT_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.UTF_HEADER; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.XML_TREE_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.getXmlWriter; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getModuleNamespace; import static org.osgi.framework.FrameworkUtil.getBundle; /** @@ -154,9 +177,11 @@ public class RestconfApiCallNode implements SvcLogicJavaPlugin { InstanceIdentifierContext<?> insIdCtx = getInsIdCtx(p, uri); String req = null; - if (p.httpMethod == POST || p.httpMethod == PUT - || p.httpMethod == PATCH) { + if (p.httpMethod != GET && p.httpMethod != DELETE) { req = serializeRequest(props, p, uri, insIdCtx); + if (p.httpMethod == PUT || p.httpMethod == PATCH) { + updateReq(req, p, insIdCtx); + } } if (req == null && p.requestBody != null) { req = p.requestBody; @@ -210,8 +235,8 @@ public class RestconfApiCallNode implements SvcLogicJavaPlugin { } if (r != null && r.code >= 300) { - throw new SvcLogicException( - String.valueOf(r.code) + ": " + r.message); + throw new SvcLogicException(valueOf(r.code) + + COLON + " " + r.message); } } @@ -316,7 +341,7 @@ public class RestconfApiCallNode implements SvcLogicJavaPlugin { */ public String getResponse(SvcLogicContext ctx, YangParameters params, String pre, HttpResponse res) { - ctx.setAttribute(pre + RES_CODE, String.valueOf(res.code)); + ctx.setAttribute(pre + RES_CODE, valueOf(res.code)); ctx.setAttribute(pre + RES_MSG, res.message); if (params.dumpHeaders && res.headers != null) { @@ -345,7 +370,7 @@ public class RestconfApiCallNode implements SvcLogicJavaPlugin { HttpResponse res = new HttpResponse(); res.code = 500; res.message = errMsg; - ctx.setAttribute(prefix + RES_CODE, String.valueOf(res.code)); + ctx.setAttribute(prefix + RES_CODE, valueOf(res.code)); ctx.setAttribute(prefix + RES_MSG, res.message); } @@ -373,4 +398,114 @@ public class RestconfApiCallNode implements SvcLogicJavaPlugin { log.debug(UPDATED_URL + retryUri.toString()); log.debug(format(COMM_FAIL, hostName, retryString)); } + + /** + * Updates request message for JSON and XML data format, when the HTTP + * method points it as PUT or PATCH. + * + * @param req current request message + * @param p YANG parameters + * @param insIdCtx instance identifier context + * @return update request message + * @throws SvcLogicException when the data format type is wrong + */ + public String updateReq(String req, YangParameters p, + InstanceIdentifierContext<?> insIdCtx) + throws SvcLogicException { + + SchemaNode schemaNode = insIdCtx.getSchemaNode(); + Namespace modNs = getModuleNamespace(schemaNode.getQName(), + insIdCtx.getSchemaContext()); + String nodeName = schemaNode.getQName().getLocalName(); + + switch (p.format) { + case JSON: + return getUpdatedJsonReq(req, nodeName, modNs.moduleName()); + + case XML: + return getXmlReqForPutOp(req, nodeName, modNs.moduleNs()); + + default: + throw new SvcLogicException(format(FORMAT_ERR, p.format)); + } + } + + /** + * Returns the updated JSON request message, when the HTTP method + * points to PUT or PATCH. + * + * @param req current JSON request message + * @param nodeName root node name + * @param modName module name of the root node + * @return update JSON request message + */ + private String getUpdatedJsonReq(String req, String nodeName, + String modName) { + Writer writer = new StringWriter(); + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(repeat(" ", 4)); + + JsonParser jsonParser = new JsonParser(); + JsonObject oldJson = (JsonObject)jsonParser.parse(req); + oldJson = remChildModName(oldJson, modName); + JsonObject newJson = new JsonObject(); + newJson.add(modName + COLON + nodeName, oldJson.deepCopy()); + + Gson gson= new Gson(); + gson.toJson(newJson, jsonWriter); + return writer.toString(); + } + + /** + * Removes module name from all the updated first level child node, if it + * is same as the root node added. + * + * @param oldJson JSON object for old request + * @param modName module name of root node + * @return JSON object for old request with updated child module name + */ + private JsonObject remChildModName(JsonObject oldJson, String modName) { + Iterator<Map.Entry<String, JsonElement>> it = oldJson.entrySet().iterator(); + Map<String, JsonElement> m = new HashMap<>(); + while (it.hasNext()) { + Map.Entry<String, JsonElement> jNode = it.next(); + if (jNode.getKey().contains(COLON)) { + String[] modArr = jNode.getKey().split(COLON); + if (modArr[0].equals(modName)) { + it.remove(); + m.put(modArr[1], jNode.getValue()); + } + } + } + if (!m.isEmpty()) { + for (Map.Entry<String, JsonElement> element : m.entrySet()) { + oldJson.add(element.getKey(), element.getValue()); + } + } + return oldJson; + } + + /** + * Returns the updated XML request message, when the HTTP method points + * to PUT or PATCH. + * + * @param req current JSON request message + * @param nodeName root node name + * @param modNs module namespace of the root node + * @return update JSON request message + * @throws SvcLogicException when XML parsing fails + */ + private String getXmlReqForPutOp(String req, String nodeName, + URI modNs) throws SvcLogicException { + req = getUpdatedXmlReq(req, nodeName, modNs.toString()); + Document oldDoc; + try { + oldDoc = DocumentHelper.parseText(req); + } catch (DocumentException e) { + throw new SvcLogicException(XML_TREE_ERR, e); + } + Writer writer = getXmlWriter( + UTF_HEADER + oldDoc.getRootElement().asXML(), "4"); + return writer.toString(); + } } diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java index b51272f7..3a0b5fb3 100644 --- a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java @@ -41,7 +41,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PUT; import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.getParameters; import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.parseParam; import static org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode.DEFAULT_MODE; @@ -66,6 +65,8 @@ public final class RestconfApiUtils { static final String COMMA = ","; + static final String COLON = ":"; + static final String HTTP_RES = "httpResponse"; static final String REST_API_URL = "restapiUrl"; @@ -97,9 +98,6 @@ public final class RestconfApiUtils { private static final String URL_SYNTAX = "The following URL cannot be " + "parsed into URI : "; - private static final String PUT_NODE_ERR = "The following URL does not " + - "contain minimum two nodes for PUT operation."; - private static final String YANG = ".yang"; private static final String YANG_FILE_ERR = "Unable to parse the YANG " + @@ -145,12 +143,6 @@ public final class RestconfApiUtils { String path = uri.getPath(); path = getParsedPath(path); - if (method == PUT) { - if (!path.contains(SLASH)) { - throw new SvcLogicException(PUT_NODE_ERR + url); - } - path = path.substring(0, path.lastIndexOf(SLASH)); - } return path; } @@ -162,15 +154,15 @@ public final class RestconfApiUtils { */ private static String getParsedPath(String path) { String firstHalf; - if (path.contains(":")) { - String[] p = path.split(":"); + if (path.contains(COLON)) { + String[] p = path.split(COLON); if (p[0].contains(SLASH)) { int slash = p[0].lastIndexOf(SLASH); firstHalf = p[0].substring(slash + 1); } else { firstHalf = p[0]; } - return firstHalf + ":" + p[1]; + return firstHalf + COLON + p[1]; } return path; } @@ -222,6 +214,13 @@ public final class RestconfApiUtils { } } + /** + * Processes all the obtained files by isolating all the YANG files from + * all the directory of the given path recursively. + * + * @param files files in the given path + * @param yangFiles YANG files list + */ private static void processFiles(File[] files, List<File> yangFiles) { for (File file : files) { if (file.isFile() && file.getName().endsWith(YANG)) { @@ -231,4 +230,20 @@ public final class RestconfApiUtils { } } } + + /** + * Returns the updated XML request message by adding root node to it. + * + * @param req XML request + * @param nodeName root node name + * @param modNs module namespace of the root node + * @return updated XML request message + */ + static String getUpdatedXmlReq(String req, String nodeName, String modNs) { + String rootNode = "\n<" + nodeName + " xmlns=\"" + modNs.toString() + + "\">\n"; + req = req.replaceFirst("\n", rootNode); + req = req + "</" + nodeName + ">"; + return req.replaceAll(">\\s+<", "><"); + } } diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerUtil.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerUtil.java index fbebd2b2..707c2944 100644 --- a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerUtil.java +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerUtil.java @@ -68,24 +68,12 @@ public final class DfSerializerUtil { static final String XML_LIS_ERR = "The XML serializer doesn't have XML " + "listener"; - static final String JSON_TREE_ERR = "Unable to form JSON tree object from" + - " the JSON body provided."; - - static final String XML_TREE_ERR = "Unable to form XML tree object from " + - "the XML body provided."; - - static final String FORMAT_ERR = "Only JSON and XML formats are supported" + - ". %s is not supported"; - static final String PROP_NODE_ERR = "The property node doesn't have " + "schema node bound to it."; static final String DF_ERR = "Type mismatch for the node %s. The schema " + "node does not match with the data format node type %s."; - static final String UTF_HEADER = "<?xml version=\"1.0\" " + - "encoding=\"UTF-8\"?>"; - static final String XML_PREFIX = "yangid"; private static final String YES = "yes"; @@ -98,6 +86,30 @@ public final class DfSerializerUtil { private static final String URI_ERR = "Unable to parse the URI"; + /** + * Data format error message for unsupported types. + */ + public static final String FORMAT_ERR = "Only JSON and XML formats are " + + "supported. %s is not supported"; + + /** + * UTF header message for XML data format message. + */ + public static final String UTF_HEADER = "<?xml version=\"1.0\" " + + "encoding=\"UTF-8\"?>"; + + /** + * Error message when a JSON tree creation fails. + */ + public static final String JSON_TREE_ERR = "Unable to form JSON tree " + + "object from the JSON body provided."; + + /** + * Error message when a XML tree creation fails. + */ + public static final String XML_TREE_ERR = "Unable to form XML tree object" + + " from the XML body provided."; + //No instantiation. private DfSerializerUtil() { } @@ -110,7 +122,7 @@ public final class DfSerializerUtil { * @return writer with XML * @throws SvcLogicException when transformation of source fails */ - static Writer getXmlWriter(String input, String indent) + public static Writer getXmlWriter(String input, String indent) throws SvcLogicException { try { Transformer transformer = TransformerFactory.newInstance() diff --git a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeUtils.java b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeUtils.java index d7b4818b..ccc4f2bd 100644 --- a/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeUtils.java +++ b/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeUtils.java @@ -340,7 +340,15 @@ public final class MdsalPropertiesNodeUtils { return null; } - private static Namespace getModuleNamespace(QName qName, SchemaContext ctx) + /** + * Returns module namespace from a given qName. + * + * @param qName qName of a node + * @param ctx schema context + * @return module namespace of the node + * @throws SvcLogicException when the module is not available + */ + public static Namespace getModuleNamespace(QName qName, SchemaContext ctx) throws SvcLogicException { Optional<Module> module = ctx.findModule(qName.getModule()); if (!module.isPresent()) { |