GET Single element JSON lists use {list:[element]}, not {list:element}.
This commit is contained in:
parent
99abac76a7
commit
9914847d6a
4 changed files with 76 additions and 60 deletions
|
|
@ -3,6 +3,11 @@
|
||||||
## 3.5.0 (Upcoming)
|
## 3.5.0 (Upcoming)
|
||||||
|
|
||||||
### Major changes:
|
### Major changes:
|
||||||
|
* Major Restconf feature update to compy to RFC 8040. Thanks Stephen Jones for getting right.
|
||||||
|
* GET well-known, top-level resource, yang library version,
|
||||||
|
* PUT whole datastore, check for different keys in put lists.
|
||||||
|
* GET Single element JSON lists use {list:[element]}, not {list:element}.
|
||||||
|
|
||||||
### Minor changes:
|
### Minor changes:
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -11,9 +16,8 @@
|
||||||
* Configuration files (non-XML) prior to 3.3.3. As enabled with `configure --with-config-compat`. The template clicon.conf.cpp files are also removed.
|
* Configuration files (non-XML) prior to 3.3.3. As enabled with `configure --with-config-compat`. The template clicon.conf.cpp files are also removed.
|
||||||
* Clixon XML C-lib prior to 3.4.0. As enabled with `configure --with-xml-compat`
|
* Clixon XML C-lib prior to 3.4.0. As enabled with `configure --with-xml-compat`
|
||||||
|
|
||||||
* new configuration option: CLICON_RESTCONF_PRETTY
|
* New configuration option: CLICON_RESTCONF_PRETTY
|
||||||
* Changed restconf GET to return object referenced. ie, GET /restconf/data/X returns X. Thanks Stephen Jones for getting this right.
|
* Changed restconf GET to return object referenced. ie, GET /restconf/data/X returns X. Thanks Stephen Jones for getting this right.
|
||||||
* Restconf: get well-known, top-level resource, yang library version, put whole datastore, check for different keys in put lists.
|
|
||||||
|
|
||||||
* Default configure file added by Matt Smith. Config file is selected in the following priority order:
|
* Default configure file added by Matt Smith. Config file is selected in the following priority order:
|
||||||
* Provide -f option when starting a program.
|
* Provide -f option when starting a program.
|
||||||
|
|
|
||||||
|
|
@ -77,9 +77,10 @@
|
||||||
|
|
||||||
enum array_element_type{
|
enum array_element_type{
|
||||||
NO_ARRAY=0,
|
NO_ARRAY=0,
|
||||||
FIRST_ARRAY,
|
FIRST_ARRAY, /* [a, */
|
||||||
MIDDLE_ARRAY,
|
MIDDLE_ARRAY, /* a, */
|
||||||
LAST_ARRAY,
|
LAST_ARRAY, /* a] */
|
||||||
|
SINGLE_ARRAY, /* [a] */
|
||||||
BODY_ARRAY
|
BODY_ARRAY
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -143,6 +144,9 @@ arraytype2str(enum array_element_type lt)
|
||||||
case LAST_ARRAY:
|
case LAST_ARRAY:
|
||||||
return "last";
|
return "last";
|
||||||
break;
|
break;
|
||||||
|
case SINGLE_ARRAY:
|
||||||
|
return "single";
|
||||||
|
break;
|
||||||
case BODY_ARRAY:
|
case BODY_ARRAY:
|
||||||
return "body";
|
return "body";
|
||||||
break;
|
break;
|
||||||
|
|
@ -158,11 +162,13 @@ array_eval(cxobj *xprev,
|
||||||
enum array_element_type array = NO_ARRAY;
|
enum array_element_type array = NO_ARRAY;
|
||||||
int eqprev=0;
|
int eqprev=0;
|
||||||
int eqnext=0;
|
int eqnext=0;
|
||||||
|
yang_stmt *ys;
|
||||||
|
|
||||||
if (xml_type(x)!=CX_ELMNT){
|
if (xml_type(x)!=CX_ELMNT){
|
||||||
array=BODY_ARRAY;
|
array=BODY_ARRAY;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
ys = xml_spec(x);
|
||||||
if (xnext &&
|
if (xnext &&
|
||||||
xml_type(xnext)==CX_ELMNT &&
|
xml_type(xnext)==CX_ELMNT &&
|
||||||
strcmp(xml_name(x),xml_name(xnext))==0)
|
strcmp(xml_name(x),xml_name(xnext))==0)
|
||||||
|
|
@ -177,6 +183,8 @@ array_eval(cxobj *xprev,
|
||||||
array = LAST_ARRAY;
|
array = LAST_ARRAY;
|
||||||
else if (eqnext)
|
else if (eqnext)
|
||||||
array = FIRST_ARRAY;
|
array = FIRST_ARRAY;
|
||||||
|
else if (ys && ys->ys_keyword == Y_LIST)
|
||||||
|
array = SINGLE_ARRAY;
|
||||||
else
|
else
|
||||||
array = NO_ARRAY;
|
array = NO_ARRAY;
|
||||||
done:
|
done:
|
||||||
|
|
@ -228,29 +236,29 @@ json_escape(char *str)
|
||||||
* The following matrix explains how the mapping is done.
|
* The following matrix explains how the mapping is done.
|
||||||
* You need to understand what arraytype means (no/first/middle/last)
|
* You need to understand what arraytype means (no/first/middle/last)
|
||||||
* and what childtype is (null,body,any)
|
* and what childtype is (null,body,any)
|
||||||
+---------+--------------+--------------+--------------+
|
+----------+--------------+--------------+--------------+
|
||||||
|array,leaf| null | body | any |
|
|array,leaf| null | body | any |
|
||||||
+---------+--------------+--------------+--------------+
|
+----------+--------------+--------------+--------------+
|
||||||
|no | <a/> |<a>1</a> |<a><b/></a> |
|
|no | <a/> |<a>1</a> |<a><b/></a> |
|
||||||
| | | | |
|
| | | | |
|
||||||
| json: |\ta:null |\ta: |\ta:{\n |
|
| json: |\ta:null |\ta: |\ta:{\n |
|
||||||
| | | |\n} |
|
| | | |\n} |
|
||||||
+---------+--------------+--------------+--------------+
|
+----------+--------------+--------------+--------------+
|
||||||
|first |<a/><a.. |<a>1</a><a.. |<a><b/></a><a.|
|
|first |<a/><a.. |<a>1</a><a.. |<a><b/></a><a.|
|
||||||
| | | | |
|
| | | | |
|
||||||
| json: |\ta:[\n\tnull |\ta:[\n\t |\ta:[\n\t{\n |
|
| json: |\ta:[\n\tnull |\ta:[\n\t |\ta:[\n\t{\n |
|
||||||
| | | |\n\t} |
|
| | | |\n\t} |
|
||||||
+---------+--------------+--------------+--------------+
|
+----------+--------------+--------------+--------------+
|
||||||
|middle |..a><a/><a.. |.a><a>1</a><a.| |
|
|middle |..a><a/><a.. |.a><a>1</a><a.| |
|
||||||
| | | | |
|
| | | | |
|
||||||
| json: |\tnull |\t |\t{a |
|
| json: |\tnull |\t |\t{a |
|
||||||
| | | |\n\t} |
|
| | | |\n\t} |
|
||||||
+---------+--------------+--------------+--------------+
|
+----------+--------------+--------------+--------------+
|
||||||
|last |..a></a> |..a><a>1</a> | |
|
|last |..a></a> |..a><a>1</a> | |
|
||||||
| | | | |
|
| | | | |
|
||||||
| json: |\tnull |\t |\t{a |
|
| json: |\tnull |\t |\t{a |
|
||||||
| |\n\t] |\n\t] |\n\t}\t] |
|
| |\n\t] |\n\t] |\n\t}\t] |
|
||||||
+---------+--------------+--------------+--------------+
|
+----------+--------------+--------------+--------------+
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml2json1_cbuf(cbuf *cb,
|
xml2json1_cbuf(cbuf *cb,
|
||||||
|
|
@ -299,6 +307,7 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FIRST_ARRAY:
|
case FIRST_ARRAY:
|
||||||
|
case SINGLE_ARRAY:
|
||||||
cprintf(cb, "%*s\"%s\": ",
|
cprintf(cb, "%*s\"%s\": ",
|
||||||
pretty?(level*JSON_INDENT):0, "",
|
pretty?(level*JSON_INDENT):0, "",
|
||||||
xml_name(x));
|
xml_name(x));
|
||||||
|
|
@ -387,6 +396,7 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SINGLE_ARRAY:
|
||||||
case LAST_ARRAY:
|
case LAST_ARRAY:
|
||||||
switch (childt){
|
switch (childt){
|
||||||
case NULL_CHILD:
|
case NULL_CHILD:
|
||||||
|
|
@ -519,6 +529,8 @@ xml2json_cbuf_vec(cbuf *cb,
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*
|
*
|
||||||
|
* @note yang is necessary to translate to one-member lists,
|
||||||
|
* eg if a is a yang LIST <a>0</a> -> {"a":["0"]} and not {"a":"0"}
|
||||||
* @code
|
* @code
|
||||||
* if (xml2json(stderr, xn, 0) < 0)
|
* if (xml2json(stderr, xn, 0) < 0)
|
||||||
* goto err;
|
* goto err;
|
||||||
|
|
@ -645,7 +657,7 @@ json_parse_str(char *str,
|
||||||
/*! Read a JSON definition from file and parse it into a parse-tree.
|
/*! Read a JSON definition from file and parse it into a parse-tree.
|
||||||
*
|
*
|
||||||
* @param[in] fd A file descriptor containing the JSON file (as ASCII characters)
|
* @param[in] fd A file descriptor containing the JSON file (as ASCII characters)
|
||||||
* @param[in] yspec Yang specification, or NULL
|
* @param[in] yspec Yang specification, or NULL XXX Not yet used
|
||||||
* @param[in,out] xt Pointer to (XML) parse tree. If empty, create.
|
* @param[in,out] xt Pointer to (XML) parse tree. If empty, create.
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error with clicon_err called
|
* @retval -1 Error with clicon_err called
|
||||||
|
|
@ -722,7 +734,7 @@ json_parse_file(int fd,
|
||||||
/*
|
/*
|
||||||
* Turn this on to get a json parse and pretty print test program
|
* Turn this on to get a json parse and pretty print test program
|
||||||
* Usage: xpath
|
* Usage: xpath
|
||||||
* read xml from input
|
* read json from input
|
||||||
* Example compile:
|
* Example compile:
|
||||||
gcc -g -o json -I. -I../clixon ./clixon_json.c -lclixon -lcligen
|
gcc -g -o json -I. -I../clixon ./clixon_json.c -lclixon -lcligen
|
||||||
* Example run:
|
* Example run:
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ module example{
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# This is a fixed 'state' implemented in routing_backend. It is assumed to be always there
|
# This is a fixed 'state' implemented in routing_backend. It is assumed to be always there
|
||||||
state='{"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}'
|
state='{"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": "42"}\]}}'
|
||||||
|
|
||||||
# kill old backend (if any)
|
# kill old backend (if any)
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
|
|
@ -109,7 +109,7 @@ if [ -z "$match" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "restconf get data/interfaces-state/interface=eth0 json"
|
new "restconf get data/interfaces-state/interface=eth0 json"
|
||||||
expectfn "curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0" '{"interface": {"name": "eth0","type": "eth","if-index": "42"}}'
|
expectfn "curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0" '{"interface": \[{"name": "eth0","type": "eth","if-index": "42"}\]}'
|
||||||
|
|
||||||
new "restconf get state operation eth0 xml"
|
new "restconf get state operation eth0 xml"
|
||||||
# Cant get shell macros to work, inline matching from lib.sh
|
# Cant get shell macros to work, inline matching from lib.sh
|
||||||
|
|
@ -137,7 +137,7 @@ new "restconf Add subtree to datastore using POST"
|
||||||
expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' ""
|
expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' ""
|
||||||
|
|
||||||
new "restconf Check interfaces eth/0/0 added"
|
new "restconf Check interfaces eth/0/0 added"
|
||||||
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}},"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}
|
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": "true"}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": "42"}\]}}
|
||||||
$'
|
$'
|
||||||
|
|
||||||
new "restconf delete interfaces"
|
new "restconf delete interfaces"
|
||||||
|
|
@ -150,7 +150,7 @@ new "restconf Add interfaces subtree eth/0/0 using POST"
|
||||||
expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces' ""
|
expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces' ""
|
||||||
|
|
||||||
new "restconf Check eth/0/0 added"
|
new "restconf Check eth/0/0 added"
|
||||||
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}},"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}
|
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": "true"}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": "42"}\]}}
|
||||||
$'
|
$'
|
||||||
|
|
||||||
new "restconf Re-post eth/0/0 which should generate error"
|
new "restconf Re-post eth/0/0 which should generate error"
|
||||||
|
|
@ -163,7 +163,7 @@ new "Add nothing using POST"
|
||||||
expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "data is in some way badly formed"
|
expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "data is in some way badly formed"
|
||||||
|
|
||||||
new "restconf Check description added"
|
new "restconf Check description added"
|
||||||
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","description": "The-first-interface","type": "eth","enabled": "true"}}
|
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","description": "The-first-interface","type": "eth","enabled": "true"}\]}
|
||||||
$'
|
$'
|
||||||
|
|
||||||
new "restconf delete eth/0/0"
|
new "restconf delete eth/0/0"
|
||||||
|
|
@ -179,7 +179,7 @@ new "restconf Add subtree eth/0/0 using PUT"
|
||||||
expectfn 'curl -s -X PUT -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
|
expectfn 'curl -s -X PUT -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
|
||||||
|
|
||||||
new "restconf get subtree"
|
new "restconf get subtree"
|
||||||
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}},"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}
|
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": "true"}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": "42"}\]}}
|
||||||
$'
|
$'
|
||||||
|
|
||||||
new "restconf rpc using POST json"
|
new "restconf rpc using POST json"
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,10 @@ new "restconf POST initial tree"
|
||||||
expectfn 'curl -s -X POST -d {"interfaces-config":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' ""
|
expectfn 'curl -s -X POST -d {"interfaces-config":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' ""
|
||||||
|
|
||||||
new "restconf GET datastore"
|
new "restconf GET datastore"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"interfaces-config": {"interface": {"name": "local0","type": "regular"}}}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"interfaces-config": {"interface": \[{"name": "local0","type": "regular"}\]}}}'
|
||||||
|
|
||||||
new "restconf GET interface"
|
new "restconf GET interface"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/interfaces-config/interface=local0" '{"interface": {"name": "local0","type": "regular"}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/interfaces-config/interface=local0" '{"interface": \[{"name": "local0","type": "regular"}\]}'
|
||||||
|
|
||||||
new "restconf GET if-type"
|
new "restconf GET if-type"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/interfaces-config/interface=local0/type" '{"type": "regular"}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/interfaces-config/interface=local0/type" '{"type": "regular"}'
|
||||||
|
|
@ -97,13 +97,13 @@ new "restconf PUT initial datastore"
|
||||||
expectfn 'curl -s -X PUT -d {"data":{"interfaces-config":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' ""
|
expectfn 'curl -s -X PUT -d {"data":{"interfaces-config":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' ""
|
||||||
|
|
||||||
new "restconf GET datastore"
|
new "restconf GET datastore"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"interfaces-config": {"interface": {"name": "local0","type": "regular"}}}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"interfaces-config": {"interface": \[{"name": "local0","type": "regular"}\]}}}'
|
||||||
|
|
||||||
new "restconf PUT change interface"
|
new "restconf PUT change interface"
|
||||||
expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/interfaces-config/interface=local0' ""
|
expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/interfaces-config/interface=local0' ""
|
||||||
|
|
||||||
new "restconf GET datastore"
|
new "restconf GET datastore atm"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"interfaces-config": {"interface": {"name": "local0","type": "atm0"}}}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"interfaces-config": {"interface": \[{"name": "local0","type": "atm0"}\]}}}'
|
||||||
|
|
||||||
new "restconf PUT add interface"
|
new "restconf PUT add interface"
|
||||||
expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/interfaces-config/interface=TEST' ""
|
expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/interfaces-config/interface=TEST' ""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue