diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c
index 46708588..84289dd4 100644
--- a/apps/restconf/restconf_methods.c
+++ b/apps/restconf/restconf_methods.c
@@ -1269,9 +1269,8 @@ api_operations_post_input(clicon_handle h,
xml_name_set(xdata, "data");
/* Here xdata is:
* ...
- * Validate that exactly only tag
*/
-#if 0
+#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xdata, 0, 0) < 0)
@@ -1279,6 +1278,7 @@ api_operations_post_input(clicon_handle h,
clicon_debug(1, "%s DATA:%s", __FUNCTION__, cbuf_get(ccc));
}
#endif
+ /* Validate that exactly only tag */
if ((xinput = xml_child_i_type(xdata, 0, CX_ELMNT)) == NULL ||
strcmp(xml_name(xinput),"input") != 0 ||
xml_child_nr_type(xdata, CX_ELMNT) != 1){
@@ -1356,8 +1356,9 @@ api_operations_post_output(clicon_handle h,
cxobj *x;
cxobj *xok;
cbuf *cbret = NULL;
- int ret;
-
+ int isempty;
+
+ // clicon_debug(1, "%s", __FUNCTION__);
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
@@ -1380,7 +1381,7 @@ api_operations_post_output(clicon_handle h,
/* 9. Translate to restconf RPC data */
xml_name_set(xoutput, "output");
/* xoutput should now look: */
-#if 0
+#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xoutput, 0, 0) < 0)
@@ -1388,56 +1389,19 @@ api_operations_post_output(clicon_handle h,
clicon_debug(1, "%s XOUTPUT:%s", __FUNCTION__, cbuf_get(ccc));
}
#endif
- /* Validate output (in case handlers are wrong) */
- if (youtput==NULL){
- /* Special case, no yang output
- * RFC 7950 7.14.4
- * If the RPC operation invocation succeeded and no output parameters
- * are returned, the contains a single element
- * RFC 8040 3.6.2
- * If the "rpc" statement has no "output" section, the response message
- * MUST NOT include a message-body and MUST send a "204 No Content"
- * status-line instead.
- */
- if ((xok = xml_child_i_type(xoutput, 0, CX_ELMNT)) == NULL ||
- strcmp(xml_name(xok),"ok") != 0 ||
- xml_child_nr_type(xoutput, CX_ELMNT) != 1){
- /* Internal error - invalid output from rpc handler */
- if (xok){
- if (netconf_operation_failed_xml(&xerr, "application",
- "Internal error: Empty RPC reply is not ok") < 0)
- goto done;
- }
- else
- if (netconf_operation_failed_xml(&xerr, "application",
- "Internal error: Empty RPC reply should have OK") < 0)
- goto done;
- if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
- clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
- goto done;
- }
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
- goto done;
- goto fail;
- }
- FCGX_SetExitStatus(204, r->out); /* OK */
- FCGX_FPrintF(r->out, "Status: 204 No Content\r\n");
- FCGX_FPrintF(r->out, "\r\n");
- goto fail;
- }
- else{
+
+ /* Sanity check of outgoing XML
+ * For now, skip outgoing checks.
+ * (1) Does not handle properly
+ * (2) Uncertain how validation errors should be logged/handled
+ */
+ if (youtput!=NULL){
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
- if (0){
- /* Sanity check of outgoing XML
- * For now, skip outgoing checks.
- * (1) Does not handle properly
- * (2) Uncertain how validation errors should be logged/handled
- */
+#if 0
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if ((ret = xml_yang_validate_all(xoutput, cbret)) < 0)
goto done;
-
if (ret == 1 &&
(ret = xml_yang_validate_add(xoutput, cbret)) < 0)
goto done;
@@ -1452,14 +1416,34 @@ api_operations_post_output(clicon_handle h,
goto done;
goto fail;
}
- }
- /* Clear namespace of parameters */
- x = NULL;
- while ((x = xml_child_each(xoutput, x, CX_ELMNT)) != NULL) {
- if ((xa = xml_find_type(x, NULL, "xmlns", CX_ATTR)) != NULL)
- if (xml_purge(xa) < 0)
- goto done;
- }
+#endif
+ }
+ /* Special case, no yang output (single - or empty body?)
+ * RFC 7950 7.14.4
+ * If the RPC operation invocation succeeded and no output parameters
+ * are returned, the contains a single element
+ * RFC 8040 3.6.2
+ * If the "rpc" statement has no "output" section, the response message
+ * MUST NOT include a message-body and MUST send a "204 No Content"
+ * status-line instead.
+ */
+ isempty = xml_child_nr_type(xoutput, CX_ELMNT) == 0 ||
+ (xml_child_nr_type(xoutput, CX_ELMNT) == 1 &&
+ (xok = xml_child_i_type(xoutput, 0, CX_ELMNT)) != NULL &&
+ strcmp(xml_name(xok),"ok")==0);
+ if (isempty) {
+ /* Internal error - invalid output from rpc handler */
+ FCGX_SetExitStatus(204, r->out); /* OK */
+ FCGX_FPrintF(r->out, "Status: 204 No Content\r\n");
+ FCGX_FPrintF(r->out, "\r\n");
+ goto fail;
+ }
+ /* Clear namespace of parameters */
+ x = NULL;
+ while ((x = xml_child_each(xoutput, x, CX_ELMNT)) != NULL) {
+ if ((xa = xml_find_type(x, NULL, "xmlns", CX_ATTR)) != NULL)
+ if (xml_purge(xa) < 0)
+ goto done;
}
/* Set namespace on output */
if (xmlns_set(xoutput, NULL, namespace) < 0)
@@ -1643,7 +1627,7 @@ api_operations_post(clicon_handle h,
}
/* Here xtop is:
42 */
-#if 0
+#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xtop, 0, 0) < 0)
@@ -1652,8 +1636,8 @@ api_operations_post(clicon_handle h,
__FUNCTION__, cbuf_get(ccc));
}
#endif
- /* 6. Validate outgoing RPC and fill in defaults */
- if (xml_spec_populate_rpc(h, xtop, yspec) < 0)
+ /* 6. Validate incoming RPC and fill in defaults */
+ if (xml_spec_populate_rpc(h, xtop, yspec) < 0) /* */
goto done;
if ((ret = xml_yang_validate_rpc(xtop, cbret)) < 0)
goto done;
@@ -1710,7 +1694,7 @@ api_operations_post(clicon_handle h,
/* 8. Receive reply from local/backend handler as Netconf RPC
* 0
*/
-#if 0
+#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xret, 0, 0) < 0)
diff --git a/example/clixon-example@2019-01-13.yang b/example/clixon-example@2019-01-13.yang
index 40e53cc6..c664a0ab 100644
--- a/example/clixon-example@2019-01-13.yang
+++ b/example/clixon-example@2019-01-13.yang
@@ -76,7 +76,20 @@ module clixon-example {
}
}
rpc empty {
- description "Smallest possible RPC with no input or output";
+ description "Smallest possible RPC with no input or output sections";
+ }
+ rpc optional {
+ description "Small RPC with optional input and output";
+ input {
+ leaf x {
+ type string;
+ }
+ }
+ output {
+ leaf x {
+ type string;
+ }
+ }
}
rpc example {
description "Some example input/output for testing RFC7950 7.14.
diff --git a/example/example_backend.c b/example/example_backend.c
index 615d7937..e0adc3ff 100644
--- a/example/example_backend.c
+++ b/example/example_backend.c
@@ -156,19 +156,20 @@ example_rpc(clicon_handle h, /* Clicon handle */
goto done;
}
cprintf(cbret, "");
- while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) {
- if (xmlns_set(x, NULL, namespace) < 0)
- goto done;
- if (clicon_xml2cbuf(cbret, x, 0, 0) < 0)
- goto done;
- }
+ if (!xml_child_nr_type(xe, CX_ELMNT))
+ cprintf(cbret, "");
+ else while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) {
+ if (xmlns_set(x, NULL, namespace) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cbret, x, 0, 0) < 0)
+ goto done;
+ }
cprintf(cbret, "");
retval = 0;
done:
return retval;
}
-
/*! Called to get state data from plugin
* @param[in] h Clicon handle
* @param[in] xpath String with XPATH syntax. or NULL for all
@@ -338,6 +339,12 @@ clixon_plugin_init(clicon_handle h)
"empty"/* Xml tag when callback is made */
) < 0)
goto done;
+ /* Same as example but with optional input/output */
+ if (rpc_callback_register(h, example_rpc,
+ NULL,
+ "optional"/* Xml tag when callback is made */
+ ) < 0)
+ goto done;
if (rpc_callback_register(h, example_rpc,
NULL,
"example"/* Xml tag when callback is made */
@@ -349,4 +356,3 @@ clixon_plugin_init(clicon_handle h)
done:
return NULL;
}
-
diff --git a/test/test_restconf.sh b/test/test_restconf.sh
index d0aabca5..6f41f6f7 100755
--- a/test/test_restconf.sh
+++ b/test/test_restconf.sh
@@ -30,27 +30,6 @@ cat < $cfg
EOF
-cat < $fyang
-module mymod{
- yang-version 1.1;
- namespace "urn:example:my";
- prefix me;
- import clixon-example {
- prefix ex;
- }
- import ietf-interfaces {
- prefix if;
- }
- import ietf-ip {
- prefix ip;
- }
- import ietf-inet-types {
- prefix "inet";
- revision-date "2013-07-15";
- }
-}
-EOF
-
# This is a fixed 'state' implemented in routing_backend. It is assumed to be always there
state='{"clixon-example:state": {"op": "42"}}'
@@ -94,12 +73,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r
# Should be alphabetically ordered
new2 "restconf get restconf/operations. RFC8040 3.3.2 (json)"
-expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:example": null,"clixon-lib:debug": null}
+expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null}
'
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
-expect=''
+expect=''
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -275,7 +254,7 @@ new2 "restconf rpc using wrong prefix"
expecteq "$(curl -s -X POST -d '{"wrong:input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
new "restconf local client rpc using POST xml"
-ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"request":"example"}}' http://localhost/restconf/operations/clixon-example:client-rpc)
+ret=$(curl -s -i -X POST -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"request":"example"}}' http://localhost/restconf/operations/clixon-example:client-rpc)
expect=''
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
diff --git a/test/test_rpc.sh b/test/test_rpc.sh
index 464a0fc5..076ced83 100755
--- a/test/test_rpc.sh
+++ b/test/test_rpc.sh
@@ -2,6 +2,9 @@
# RPC tests
# Validate parameters in restconf and netconf, check namespaces, etc
# See rfc8040 3.6
+# Use the example application that has one mandatory input arg,
+# At the end is an alternative Yang without mandatory arg for
+# valid empty input and output.
APPNAME=example
# include err() and new() functions and creates $dir
@@ -95,7 +98,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^applicationmissing-elementsession-iderrorMandatory variable]]>]]>$'