XPath: refactored XPath match, documented localonly and prefixonly api
This commit is contained in:
parent
6c73c36fb7
commit
081a541c6b
13 changed files with 384 additions and 105 deletions
|
|
@ -656,6 +656,7 @@ get_list_pagination(clixon_handle h,
|
|||
int j;
|
||||
int ret;
|
||||
dispatcher_entry_t *htable = NULL;
|
||||
cvec *wherens = NULL;
|
||||
// int extflag = 0;
|
||||
#ifdef LIST_PAGINATION_REMAINING
|
||||
cxobj *xcache;
|
||||
|
|
@ -715,6 +716,8 @@ get_list_pagination(clixon_handle h,
|
|||
(where = xml_body(x)) != NULL){
|
||||
if (strcmp(where, "unfiltered") == 0)
|
||||
where = NULL;
|
||||
else if (xml_nsctx_node(x, &wherens) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* then the "sort-by" parameter (see Section 3.1.2) */
|
||||
if ((x = xml_find_type(xe, NULL, "sort-by", CX_ELMNT)) != NULL){
|
||||
|
|
@ -907,6 +910,8 @@ get_list_pagination(clixon_handle h,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (wherens)
|
||||
cvec_free(wherens);
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
if (cbmsg)
|
||||
|
|
|
|||
|
|
@ -214,3 +214,13 @@
|
|||
* worse case an overwrite.
|
||||
*/
|
||||
#undef SYSTEM_ONLY_CONFIG_CANDIDATE_CLEAR
|
||||
|
||||
/*! In full XPath namespace resolve, match even if namespace not resolved
|
||||
*
|
||||
* In the case of xpath lookup functions (eg xpath_vec_ctx) where nsc is defined, then
|
||||
* matching with XML requires equal namespaces.
|
||||
* However, some code is OK with the XPATH NSC being unresolved to NULL, even if the XML
|
||||
* namespace is defined.
|
||||
* This seems wrong and should be changed, but need further investigation
|
||||
*/
|
||||
#define XPATH_NS_ACCEPT_UNRESOLVED
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ static inline int clixon_debug_isset(unsigned n)
|
|||
}
|
||||
|
||||
/* Is detail set ?, return detail level 0-7 */
|
||||
static inline int clixon_debug_detail()
|
||||
static inline int clixon_debug_detail(void)
|
||||
{
|
||||
unsigned level = clixon_debug_get();
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ int xml_nsctx_node(cxobj *x, cvec **ncp);
|
|||
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
|
||||
int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp);
|
||||
int xml_nsctx_cbuf(cbuf *cb, cvec *nsc);
|
||||
int xml2ns(cxobj *x, char *localname, char **ns);
|
||||
int xml2ns(cxobj *x, char *prefix, char **ns);
|
||||
int xml2ns_recurse(cxobj *x);
|
||||
int xmlns_set(cxobj *x, char *prefix, char *ns);
|
||||
int xmlns_set_all(cxobj *x, cvec *nsc);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,9 @@ enum xp_objtype{
|
|||
XT_STRING
|
||||
};
|
||||
|
||||
/* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the
|
||||
/*! XPath context and result
|
||||
*
|
||||
* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the
|
||||
* context is determined for XPath expressions used in XSLT and XPointer respectively. The
|
||||
* context consists of:
|
||||
* a node (the context node)
|
||||
|
|
|
|||
|
|
@ -60,7 +60,10 @@
|
|||
* of the xpath-tree, which is context-dependent.
|
||||
* Best is to send it as a (read-only) parameter to the xp_eval family of functions
|
||||
* as an exlicit namespace context.
|
||||
*
|
||||
* 3) localonly flag refers to https://www.w3.org/TR/REC-xml-names/, where :
|
||||
* PrefixedName ::= Prefix ':' LocalPart
|
||||
* "localonly" means to skip the "Prefix" part, ie namespaces
|
||||
* see nodetest_eval_node() for the semantics of prefix/namespace processing
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
|
|
@ -608,7 +611,7 @@ xpath_parse(const char *xpath,
|
|||
* @param[in] xcur XML-tree where to search
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPath 1.0 syntax
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[in] localonly Skip prefix and namespace tests
|
||||
* @param[out] xrp Return XPath context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@
|
|||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_xpath_optimize.h"
|
||||
#include "clixon_xpath_function.h"
|
||||
|
|
@ -110,99 +111,148 @@ const map_str2int xpopmap[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
|
||||
/*! Eval an XPath nodetest
|
||||
/*! Eval an XPath nodetest with full namespace test
|
||||
*
|
||||
* XML x -> prefix1 + name1
|
||||
* XPATH xs -> prefix2 + name2
|
||||
* Unless name2=*, if name1 != name2 -> fail
|
||||
* Lookup(prefix1, XML) -> ns1
|
||||
* Lookup(prefix2, NSC) -> ns2
|
||||
* if ns1 = NULL -> fail
|
||||
* if ns2 = NULL -> fail (see XPATH_NS_ACCEPT_UNRESOLVED)
|
||||
* if ns1 = ns2 -> match
|
||||
* otherwise fail
|
||||
* @param[in] x XML sub-tree given by the the context node
|
||||
* @param[in] xs XPath stack of type XP_NODE or XP_NODE_FN
|
||||
* @param[in] nsc XML Namespace context as given by xpath_vec_ctx()
|
||||
* @retval 1 Match
|
||||
* @retval 0 No match
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
nodetest_eval_namespace(cxobj *x,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc)
|
||||
{
|
||||
int retval = -1;
|
||||
char *name1;
|
||||
char *prefix1;
|
||||
char *prefix2;
|
||||
char *name2;
|
||||
char *ns1 = NULL; /* xml namespace */
|
||||
char *ns2 = NULL; /* xpath namespace */
|
||||
|
||||
/* XML x -> prefix1 + name1 */
|
||||
prefix1 = xml_prefix(x);
|
||||
name1 = xml_name(x);
|
||||
/* XPATH xs -> prefix2 + name2 */
|
||||
prefix2 = xs->xs_s0;
|
||||
name2 = xs->xs_s1;
|
||||
clixon_debug(CLIXON_DBG_XPATH | CLIXON_DBG_DETAIL, "%s %s", name1, name2);
|
||||
if (strcmp(name2, "*") != 0){
|
||||
/* if name1 != name2 -> fail */
|
||||
if (strcmp(name1, name2) != 0)
|
||||
goto fail;
|
||||
}
|
||||
/* get namespace of xml tree */
|
||||
if (xml2ns(x, prefix1, &ns1) < 0)
|
||||
goto done;
|
||||
if (ns1 == NULL)
|
||||
goto fail;
|
||||
ns2 = xml_nsctx_get(nsc, prefix2);
|
||||
if (ns2 == NULL){
|
||||
#ifdef XPATH_NS_ACCEPT_UNRESOLVED
|
||||
goto ok;
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
}
|
||||
else if (strcmp(ns1, ns2) != 0)
|
||||
goto fail;
|
||||
#ifdef XPATH_NS_ACCEPT_UNRESOLVED
|
||||
ok:
|
||||
#endif
|
||||
retval = 1;
|
||||
done: /* retval set in preceding statement */
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Eval an XPath nodetest but skip namespace tests
|
||||
*
|
||||
* XML x -> prefix1 + name1
|
||||
* XPATH xs -> prefix2 + name2
|
||||
* Unless name2=*, if name1 != name2 -> fail
|
||||
* if prefix1 and prefix2 are string-equal or both NULL -> match
|
||||
* else -> fail
|
||||
* @param[in] x XML node
|
||||
* @param[in] xs XPath stack of type XP_NODE or XP_NODE_FN
|
||||
* @retval 1 Match
|
||||
* @retval 0 No match
|
||||
* @retval -1 Error XXX: retval -1 not properly handled
|
||||
*/
|
||||
static int
|
||||
nodetest_eval_node(cxobj *x,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc)
|
||||
nodetest_eval_prefixonly(cxobj *x,
|
||||
xpath_tree *xs)
|
||||
{
|
||||
int retval = -1;
|
||||
char *name1 = xml_name(x);
|
||||
char *prefix1 = xml_prefix(x);
|
||||
char *nsxml = NULL; /* xml body namespace */
|
||||
char *nsxpath = NULL; /* xpath context namespace */
|
||||
char *prefix2 = NULL;
|
||||
char *name2 = NULL;
|
||||
char *name1;
|
||||
char *prefix1;
|
||||
char *prefix2;
|
||||
char *name2;
|
||||
int ret;
|
||||
|
||||
/* Namespaces is s0, name is s1 */
|
||||
if (strcmp(xs->xs_s1, "*")==0)
|
||||
return 1;
|
||||
/* get namespace of xml tree */
|
||||
if (xml2ns(x, prefix1, &nsxml) < 0)
|
||||
goto done;
|
||||
/* XML x -> prefix1 + name1 */
|
||||
prefix1 = xml_prefix(x);
|
||||
name1 = xml_name(x);
|
||||
/* XPATH xs -> prefix2 + name2 */
|
||||
prefix2 = xs->xs_s0;
|
||||
name2 = xs->xs_s1;
|
||||
/* Before going into namespaces, check name equality and filter out noteq */
|
||||
if (strcmp(name1, name2) != 0){
|
||||
retval = 0; /* no match */
|
||||
goto done;
|
||||
clixon_debug(CLIXON_DBG_XPATH | CLIXON_DBG_DETAIL, "%s:%s %s:%s", prefix1, name1, prefix2, name2);
|
||||
if (strcmp(name2, "*") != 0){
|
||||
/* if name1 != name2 -> fail */
|
||||
if (strcmp(name1, name2) != 0)
|
||||
goto fail;
|
||||
}
|
||||
/* Here names are equal
|
||||
* Now look for namespaces
|
||||
* 1) prefix1 and prefix2 point to same namespace <<-- try this first
|
||||
* 2) prefix1 is equal to prefix2 <<-- then try this
|
||||
* (1) is strict yang xml
|
||||
* (2) without yang
|
||||
*/
|
||||
if (nsc != NULL) { /* solution (1) */
|
||||
nsxpath = xml_nsctx_get(nsc, prefix2);
|
||||
if (nsxml != NULL && nsxpath != NULL)
|
||||
retval = (strcmp(nsxml, nsxpath) == 0);
|
||||
else if (nsxpath == NULL){
|
||||
/* We have a namespace from xml, but none in yang.
|
||||
* This can happen in eg augments and ../foo, where foo is
|
||||
* augmented from another namespace
|
||||
*/
|
||||
ret = clicon_strcmp(prefix1, prefix2);
|
||||
if (ret != 0)
|
||||
goto fail;
|
||||
retval = 1;
|
||||
}
|
||||
else
|
||||
retval = (nsxml == nsxpath); /* True only if both are NULL */
|
||||
}
|
||||
else{ /* solution (2) */
|
||||
if (prefix1 == NULL && prefix2 == NULL)
|
||||
retval = 1;
|
||||
else if (prefix1 == NULL || prefix2 == NULL)
|
||||
retval = 0;
|
||||
else
|
||||
retval = strcmp(prefix1, prefix2) == 0;
|
||||
}
|
||||
#if 0 /* debugging */
|
||||
/* If retval == 0 here, then there is name match, but not ns match */
|
||||
if (retval == 0){
|
||||
fprintf(stderr, "%s NOMATCH xml: (%s)%s\n\t\t xpath: (%s)%s\n", __FUNCTION__,
|
||||
name1, nsxml,
|
||||
name2, nsxpath);
|
||||
}
|
||||
#endif
|
||||
done: /* retval set in preceding statement */
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Eval an XPath nodetest but skip prefix and namespace tests
|
||||
*
|
||||
* This is NOT according to standard
|
||||
* If name2 = "*" -> match # A node test * is true for any node of the principal node type.
|
||||
* If name1 = name2 -> match (string-equal or both NULL)
|
||||
* @param[in] x XML node
|
||||
* @param[in] xs XPath stack of type XP_NODE or XP_NODE_FN
|
||||
* @retval 1 Match
|
||||
* @retval 0 No match
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
nodetest_eval_node_localonly(cxobj *x,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc)
|
||||
nodetest_eval_localonly(cxobj *x,
|
||||
xpath_tree *xs)
|
||||
{
|
||||
int retval = -1;
|
||||
char *name1 = xml_name(x);
|
||||
char *name2 = NULL;
|
||||
char *name1;
|
||||
char *name2;
|
||||
|
||||
/* Namespaces is s0, name is s1 */
|
||||
if (strcmp(xs->xs_s1, "*")==0){
|
||||
name1 = xml_name(x);
|
||||
name2 = xs->xs_s1;
|
||||
clixon_debug(CLIXON_DBG_XPATH | CLIXON_DBG_DETAIL, "%s %s", name1, name2);
|
||||
if (strcmp(name2, "*")==0){
|
||||
retval = 1;
|
||||
goto done;
|
||||
}
|
||||
name2 = xs->xs_s1;
|
||||
/* Before going into namespaces, check name equality and filter out noteq */
|
||||
/* Check name only */
|
||||
if (strcmp(name1, name2) == 0){
|
||||
retval = 1;
|
||||
goto done;
|
||||
|
|
@ -234,9 +284,11 @@ nodetest_eval(cxobj *x,
|
|||
|
||||
if (xs->xs_type == XP_NODE){
|
||||
if (localonly)
|
||||
retval = nodetest_eval_node_localonly(x, xs, nsc);
|
||||
retval = nodetest_eval_localonly(x, xs);
|
||||
else if (nsc == NULL)
|
||||
retval = nodetest_eval_prefixonly(x, xs);
|
||||
else
|
||||
retval = nodetest_eval_node(x, xs, nsc);
|
||||
retval = nodetest_eval_namespace(x, xs, nsc);
|
||||
}
|
||||
else if (xs->xs_type == XP_NODE_FN){
|
||||
switch (xs->xs_int){
|
||||
|
|
@ -1203,7 +1255,7 @@ xp_eval(xp_ctx *xc,
|
|||
goto ok;
|
||||
break;
|
||||
case XPATHFN_DEREF:
|
||||
if (xp_function_deref(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
if (xp_function_deref(xc, xs->xs_c0, nsc, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
|
|
@ -1223,7 +1275,7 @@ xp_eval(xp_ctx *xc,
|
|||
goto ok;
|
||||
break;
|
||||
case XPATHFN_POSITION:
|
||||
if (xp_function_position(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
if (xp_function_position(xc, xs->xs_c0, nsc, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
|
|
@ -1288,12 +1340,12 @@ xp_eval(xp_ctx *xc,
|
|||
goto ok;
|
||||
break;
|
||||
case XPATHFN_TRUE:
|
||||
if (xp_function_true(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
if (xp_function_true(xc, xs->xs_c0, nsc, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XPATHFN_FALSE:
|
||||
if (xp_function_false(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
if (xp_function_false(xc, xs->xs_c0, nsc, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -280,7 +280,6 @@ xp_function_re_match(xp_ctx *xc,
|
|||
* @param[in] xc0 Incoming context
|
||||
* @param[in] xs XPath node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[out] xrp Resulting context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
|
|
@ -290,7 +289,6 @@ int
|
|||
xp_function_deref(xp_ctx *xc0,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -587,7 +585,6 @@ xp_function_bit_is_set(xp_ctx *xc,
|
|||
* @param[in] xc Incoming context
|
||||
* @param[in] xs XPath node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[out] xrp Resulting context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
|
|
@ -596,7 +593,6 @@ int
|
|||
xp_function_position(xp_ctx *xc,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -1253,7 +1249,6 @@ int
|
|||
xp_function_true(xp_ctx *xc,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -1280,7 +1275,6 @@ int
|
|||
xp_function_false(xp_ctx *xc,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
|
|||
|
|
@ -95,10 +95,10 @@ const char *xp_fnname_int2str(enum clixon_xpath_function code);
|
|||
|
||||
int xp_function_current(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_re_match(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_deref(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_deref(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
|
||||
int xp_function_derived_from(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, int self, xp_ctx **xrp);
|
||||
int xp_function_bit_is_set(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_position(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_position(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
|
||||
int xp_function_count(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_name(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_string(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
|
|
@ -109,7 +109,7 @@ int xp_function_string_length(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int
|
|||
int xp_function_translate(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_boolean(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_not(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_true(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_false(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_true(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
|
||||
int xp_function_false(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
|
||||
|
||||
#endif /* _CLIXON_XPATH_FUNCTION_H */
|
||||
|
|
|
|||
|
|
@ -242,17 +242,17 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
|
|||
new "A.3.6.2. where, match on descendent string containing a substring"
|
||||
# bob, eric, alice, lin, joe
|
||||
# Confusing: all match
|
||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where>.[contains (email-address,'@example.com')]</where></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>bob</member-id>.*<member-id>eric</member-id>.*<member-id>alice</member-id>.*<member-id>lin</member-id>.*<member-id>joe</member-id>"
|
||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where xmlns:ex=\"https://example.com/ns/example-social\">.[contains (es:email-address,'@example.com')]</where></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>bob</member-id>.*<member-id>eric</member-id>.*<member-id>alice</member-id>.*<member-id>lin</member-id>.*<member-id>joe</member-id>"
|
||||
|
||||
new "A.3.6.3. where, match on decendent timestamp starting with a substring"
|
||||
# bob, eric, alice, joe,
|
||||
# starts-with NYI, replaced with contains
|
||||
# posts//post[starts-with(timestamp,'2020')]
|
||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where>posts/post[contains(timestamp,'2020')]</where></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>bob</member-id>.*<member-id>eric</member-id>.*<member-id>alice</member-id>.*<member-id>joe</member-id>"
|
||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where xmlns:ex=\"https://example.com/ns/example-social\">es:posts/es:post[contains(es:timestamp,'2020')]</where></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>bob</member-id>.*<member-id>eric</member-id>.*<member-id>alice</member-id>.*<member-id>joe</member-id>"
|
||||
|
||||
new "A.3.9.1. All six parameters at once"
|
||||
# eric, bob
|
||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where>//post[contains(timestamp,'2020')]</where><sort-by>member-id</sort-by><direction>backwards</direction><offset>2</offset><limit>2</limit></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>eric</member-id>.*<member-id>bob</member-id>"
|
||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member\" xmlns:es=\"https://example.com/ns/example-social\"/><list-pagination xmlns=\"urn:ietf:params:xml:ns:yang:ietf-list-pagination-nc\"><where xmlns:ex=\"https://example.com/ns/example-social\">//es:post[contains(es:timestamp,'2020')]</where><sort-by>member-id</sort-by><direction>backwards</direction><offset>2</offset><limit>2</limit></list-pagination></get></rpc>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"https://example.com/ns/example-social\"><member><member-id>eric</member-id>.*<member-id>bob</member-id>"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
|
|
|
|||
|
|
@ -447,7 +447,6 @@ new "xpath /root/*/*[.='111']"
|
|||
expecteof "$clixon_util_xpath -D $DBG -f $xml4 -p /root/*/*[.='111']" 0 "" "nodeset:0:<a>111</a>1:<b>111</b>2:<a>111</a>"
|
||||
|
||||
# Try functionnames in place of node nc-names
|
||||
|
||||
new "xpath nodetest: node"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xmlfn -p "count(/root/count)")" 0 "number:1"
|
||||
|
||||
|
|
|
|||
214
test/test_xpath_crossref.sh
Executable file
214
test/test_xpath_crossref.sh
Executable file
|
|
@ -0,0 +1,214 @@
|
|||
#!/usr/bin/env bash
|
||||
# XPath crossref tests
|
||||
# Load datastrore XML as follows:
|
||||
# 1. Without prefix
|
||||
# 2. With canonical prefix
|
||||
# 3. With non-canonical prefix
|
||||
# Access using XPath:
|
||||
# 1. Without prefix
|
||||
# 2. With canonical prefix
|
||||
# 3. With non-canonical prefix
|
||||
# With namespace context:
|
||||
# 1. Yes
|
||||
# 2. No
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
: ${clixon_util_xpath:=clixon_util_xpath}
|
||||
|
||||
# XML file (alt provide it in stdin after xpath)
|
||||
xml1=$dir/xml1.xml
|
||||
xml2=$dir/xml2.xml
|
||||
xml3=$dir/xml3.xml
|
||||
|
||||
fyang=$dir/clixon-example.yang
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module clixon-example {
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:clixon";
|
||||
prefix ex;
|
||||
container table1 {
|
||||
description "1. Without prefix";
|
||||
list parameter {
|
||||
key name;
|
||||
leaf name {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
container table2 {
|
||||
description "2. With canonical prefix";
|
||||
list parameter {
|
||||
key name;
|
||||
leaf name {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
container table3 {
|
||||
description "3. With non-canonical prefix";
|
||||
list parameter {
|
||||
key name;
|
||||
leaf name {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
#
|
||||
cat <<EOF > $xml1
|
||||
<table1 xmlns="urn:example:clixon">
|
||||
<parameter>
|
||||
<name>A</name>
|
||||
</parameter>
|
||||
</table1>
|
||||
EOF
|
||||
cat <<EOF > $xml2
|
||||
<ex:table2 xmlns:ex="urn:example:clixon">
|
||||
<ex:parameter>
|
||||
<ex:name>B</ex:name>
|
||||
</ex:parameter>
|
||||
</ex:table2>
|
||||
EOF
|
||||
cat <<EOF > $xml3
|
||||
<xe:table3 xmlns:xe="urn:example:clixon">
|
||||
<xe:parameter>
|
||||
<xe:name>C</xe:name>
|
||||
</xe:parameter>
|
||||
</xe:table3>
|
||||
EOF
|
||||
|
||||
unset y
|
||||
#-------------------------------------- Name only
|
||||
prefix=xxx:
|
||||
localonly="-L"
|
||||
nsc=
|
||||
xi=1
|
||||
xml=$dir/xml$xi.xml
|
||||
new "Name only $xi"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<parameter><name>A</name></parameter>"
|
||||
|
||||
new "Name only $xi"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}xxx $y $nsc)" 0 "nodeset:$"
|
||||
|
||||
xi=2
|
||||
xml=$dir/xml$xi.xml
|
||||
new "Name only $xi"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
|
||||
|
||||
xi=3
|
||||
xml=$dir/xml$xi.xml
|
||||
new "Name only $xi"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
|
||||
|
||||
#-------------------------------------- Prefix only
|
||||
localonly=
|
||||
nsc=
|
||||
|
||||
xi=1
|
||||
xml=$dir/xml$xi.xml
|
||||
unset prefix
|
||||
new "Prefix only $xi prefix=$prefix"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<parameter><name>A</name></parameter>"
|
||||
|
||||
prefix=xe:
|
||||
new "Prefix only $xi prefix=$prefix fail"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
|
||||
|
||||
xi=2
|
||||
xml=$dir/xml$xi.xml
|
||||
unset prefix
|
||||
new "Prefix only $xi prefix=$prefix"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
|
||||
|
||||
prefix=ex:
|
||||
new "Prefix only $xi prefix=$prefix"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
|
||||
|
||||
prefix=xe:
|
||||
new "Prefix only $xi prefix=$prefix"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
|
||||
|
||||
xi=3
|
||||
xml=$dir/xml$xi.xml
|
||||
unset prefix
|
||||
new "Prefix only $xi prefix=$prefix"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
|
||||
|
||||
prefix=ex:
|
||||
new "Prefix only $xi prefix=$prefix"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
|
||||
|
||||
prefix=xe:
|
||||
new "Prefix only $xi prefix=$prefix"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
|
||||
|
||||
#-------------------------------------- Namespace
|
||||
localonly=
|
||||
nsc="-n null:urn:example:clixon"
|
||||
xi=1
|
||||
xml=$dir/xml$xi.xml
|
||||
prefix=
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<parameter><name>A</name></parameter>"
|
||||
|
||||
nsc="-n m:urn:example:clixon"
|
||||
prefix=m:
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<parameter><name>A</name></parameter>"
|
||||
|
||||
if false; then # SEE XPATH_NS_ACCEPT_UNRESOLVED
|
||||
nsc="-n n:urn:example:clixon"
|
||||
prefix=m:
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
#echo "$clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
|
||||
fi
|
||||
|
||||
xi=2
|
||||
xml=$dir/xml$xi.xml
|
||||
|
||||
nsc="-n null:urn:example:clixon"
|
||||
prefix=
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
|
||||
|
||||
nsc="-n ex:urn:example:clixon"
|
||||
prefix=ex:
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
|
||||
|
||||
nsc="-n xe:urn:example:clixon"
|
||||
prefix=xe:
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<ex:parameter><ex:name>B</ex:name></ex:parameter>"
|
||||
|
||||
nsc="-n xe:urn:example:xxx"
|
||||
prefix=xe:
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:$"
|
||||
|
||||
xi=3
|
||||
xml=$dir/xml$xi.xml
|
||||
nsc="-n null:urn:example:clixon"
|
||||
prefix=
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
|
||||
|
||||
nsc="-n ex:urn:example:clixon"
|
||||
prefix=ex:
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
|
||||
|
||||
nsc="-n xe:urn:example:clixon"
|
||||
prefix=xe:
|
||||
new "Namespace $xi prefix=$prefix nsc=$nsc"
|
||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml ${localonly} -p ${prefix}table$xi/${prefix}parameter $y $nsc)" 0 "nodeset:0:<xe:parameter><xe:name>C</xe:name></xe:parameter>"
|
||||
|
||||
rm -rf $dir
|
||||
|
||||
new "endtest"
|
||||
endtest
|
||||
Loading…
Add table
Add a link
Reference in a new issue