parser data UPDATE parsing RESTCONF messages directly
diff --git a/src/common.h b/src/common.h
index 3c8f78c..8364b21 100644
--- a/src/common.h
+++ b/src/common.h
@@ -59,6 +59,7 @@
#define GETMACRO4(_1, _2, _3, _4, NAME, ...) NAME
#define GETMACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME
#define GETMACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
+#define GETMACRO7(_1, _2, _3, _4, _5, _6, _7, NAME, ...) NAME
/******************************************************************************
* Logger
@@ -220,8 +221,10 @@
LY_CHECK_ARG_RET1(CTX, ARG4, RETVAL)
#define LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL) LY_CHECK_ARG_RET4(CTX, ARG1, ARG2, ARG3, ARG4, RETVAL);\
LY_CHECK_ARG_RET1(CTX, ARG5, RETVAL)
-#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO6(__VA_ARGS__, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, LY_CHECK_ARG_RET3, \
- LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
+#define LY_CHECK_ARG_RET6(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, RETVAL) LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL);\
+ LY_CHECK_ARG_RET1(CTX, ARG6, RETVAL)
+#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO7(__VA_ARGS__, LY_CHECK_ARG_RET6, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, \
+ LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
#define LY_CHECK_CTX_EQUAL_RET2(CTX1, CTX2, RETVAL) if ((CTX1) && (CTX2) && ((CTX1) != (CTX2))) \
{LOGERR(CTX1, LY_EINVAL, "Different contexts mixed in a single function call."); return RETVAL;}
diff --git a/src/parser_common.c b/src/parser_common.c
index 0a035a1..693ca65 100644
--- a/src/parser_common.c
+++ b/src/parser_common.c
@@ -46,6 +46,7 @@
#include "parser_data.h"
#include "path.h"
#include "plugins_exts/metadata.h"
+#include "schema_compile_node.h"
#include "schema_features.h"
#include "set.h"
#include "tree.h"
@@ -65,6 +66,50 @@
}
LY_ERR
+lyd_parser_notif_eventtime_validate(const struct lyd_node *node)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
+ struct lysc_ctx cctx;
+ const struct lys_module *mod;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_err_item *err = NULL;
+ struct lysp_type *type_p = NULL;
+ struct lysc_pattern **patterns = NULL;
+ const char *value;
+
+ LYSC_CTX_INIT_CTX(cctx, ctx);
+
+ /* get date-and-time parsed type */
+ mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
+ assert(mod);
+ LY_ARRAY_FOR(mod->parsed->typedefs, u) {
+ if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
+ type_p = &mod->parsed->typedefs[u].type;
+ break;
+ }
+ }
+ assert(type_p);
+
+ /* compile patterns */
+ assert(type_p->patterns);
+ LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
+
+ /* validate */
+ value = lyd_get_value(node);
+ rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
+
+cleanup:
+ FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
+ if (rc && err) {
+ LOGVAL_ERRITEM(ctx, err);
+ ly_err_free(err);
+ LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
+ }
+ return rc;
+}
+
+LY_ERR
lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op)
{
const struct lyd_node *iter;
diff --git a/src/parser_data.h b/src/parser_data.h
index c72cf4d..1bad5b2 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -310,21 +310,29 @@
* @{
*/
enum lyd_type {
- LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
- LYD_TYPE_RPC_YANG, /* instance of a YANG RPC/action request with only "input" data children,
- including all parents in case of an action */
- LYD_TYPE_NOTIF_YANG, /* instance of a YANG notification, including all parents in case of a nested one */
- LYD_TYPE_REPLY_YANG, /* instance of a YANG RPC/action reply with only "output" data children,
- including all parents in case of an action */
+ LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
+ LYD_TYPE_RPC_YANG, /* instance of a YANG RPC/action request with only "input" data children,
+ including all parents in case of an action */
+ LYD_TYPE_NOTIF_YANG, /* instance of a YANG notification, including all parents in case of a nested one */
+ LYD_TYPE_REPLY_YANG, /* instance of a YANG RPC/action reply with only "output" data children,
+ including all parents in case of an action */
- LYD_TYPE_RPC_NETCONF, /* complete NETCONF RPC invocation as defined for
- [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
- [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
- LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
- [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
- LYD_TYPE_REPLY_NETCONF /* complete NETCONF RPC reply as defined for
- [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
- [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+ LYD_TYPE_RPC_NETCONF, /* complete NETCONF RPC invocation as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+ LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
+ [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
+ LYD_TYPE_REPLY_NETCONF, /* complete NETCONF RPC reply as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+
+ LYD_TYPE_RPC_RESTCONF, /* message-body of a RESTCONF operation input parameters
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.1)) */
+ LYD_TYPE_NOTIF_RESTCONF, /* RESTCONF JSON notification data
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-6.4)), to parse
+ a notification in XML, use ::LYD_TYPE_NOTIF_NETCONF */
+ LYD_TYPE_REPLY_RESTCONF /* message-body of a RESTCONF operation output parameters
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.2)) */
};
/** @} datatype */
@@ -357,6 +365,28 @@
* - @p op - must be NULL, the reply is appended to the RPC;
* Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
*
+ * - ::LYD_TYPE_RPC_RESTCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - can be both ::LYD_JSON and ::LYD_XML;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, @p parent points to the operation;
+ *
+ * - ::LYD_TYPE_NOTIF_RESTCONF:
+ * - @p parent - must be NULL, the whole notification is expected;
+ * - @p format - must be ::LYD_JSON, XML-formatted notifications are parsed using ::LYD_TYPE_NOTIF_NETCONF;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_REPLY_RESTCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - can be both ::LYD_JSON and ::LYD_XML;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, @p parent points to the operation;
+ * Note that error reply should be parsed as 'yang-data' extension data.
+ *
* @param[in] ctx libyang context.
* @param[in] parent Optional parent to connect the parsed nodes to.
* @param[in] in Input handle to read the input from.
diff --git a/src/parser_internal.h b/src/parser_internal.h
index f284bca..419d68e 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -298,6 +298,27 @@
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
/**
+ * @brief Parse JSON string as a RESTCONF message.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Expected RESTCONF data type of the data.
+ * @param[out] envp Individual parsed envelopes tree, may be returned possibly even on an error.
+ * @param[out] parsed Set to add all the parsed siblings into.
+ * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
+ struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p);
+
+/**
* @brief Parse binary LYB data as a YANG data tree.
*
* @param[in] ctx libyang context.
@@ -318,6 +339,15 @@
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
/**
+ * @brief Validate eventTime date-and-time value.
+ *
+ * @param[in] node Opaque eventTime node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_parser_notif_eventtime_validate(const struct lyd_node *node);
+
+/**
* @brief Search all the parents for an operation node, check validity based on internal parser flags.
*
* @param[in] parent Parent to connect the parsed nodes to.
diff --git a/src/parser_json.c b/src/parser_json.c
index ea9551a..4c93e86 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1862,3 +1862,255 @@
}
return rc;
}
+
+/**
+ * @brief Parse a specific JSON object into an opaque node.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @param[in] name Name of the object.
+ * @param[in] module Module name of the object, NULL if none expected.
+ * @param[in] value Whether a value is expected.
+ * @param[out] evnp Parsed envelope (opaque node).
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the specified object did not match.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *module, ly_bool value, struct lyd_node **envp)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
+ const char *nam, *prefix, *val = NULL;
+ size_t nam_len, prefix_len, val_len = 0;
+ ly_bool is_meta;
+
+ assert(status == LYJSON_OBJECT);
+
+ *envp = NULL;
+
+ r = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ if (status == LYJSON_OBJECT_CLOSED) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON object.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* process the name */
+ assert(status == LYJSON_OBJECT_NAME);
+ lydjson_parse_name(jsonctx->value, jsonctx->value_len, &nam, &nam_len, &prefix, &prefix_len, &is_meta);
+ if (is_meta) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected metadata.");
+ rc = LY_EVALID;
+ goto cleanup;
+ } else if (module && ly_strncmp(module, prefix, prefix_len)) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected module \"%.*s\" instead of \"%s\".", (int)prefix_len, prefix, module);
+ rc = LY_EVALID;
+ goto cleanup;
+ } else if (ly_strncmp(name, nam, nam_len)) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"%s\".", (int)nam_len, nam, name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ r = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ if (value) {
+ if (status != LYJSON_STRING) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_STRING),
+ lyjson_token2str(status));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ val = jsonctx->value;
+ val_len = jsonctx->value_len;
+
+ r = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+ /* create node */
+ rc = lyd_create_opaq(jsonctx->ctx, name, strlen(name), prefix, prefix_len, prefix, prefix_len, val, val_len,
+ NULL, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse all expected non-data JSON objects of a RESTCONF notification message.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @param[out] evnp Parsed envelope(s) (opaque node).
+ * @param[out] int_opts Internal options for parsing the rest of YANG data.
+ * @param[out] close_elem Number of parsed opened objects that need to be closed.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydjson_env_restconf_notif(struct lyjson_ctx *jsonctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_node *child;
+
+ assert(envp && !*envp);
+
+ /* parse "notification" */
+ r = lydjson_envelope(jsonctx, "notification", "ietf-restconf", 0, envp);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* parse "eventTime" */
+ r = lydjson_envelope(jsonctx, "eventTime", NULL, 1, &child);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* insert */
+ lyd_insert_node(*envp, NULL, child, 0);
+
+ /* validate value */
+ r = lyd_parser_notif_eventtime_validate(child);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* RESTCONF notification */
+ *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_NOTIF;
+ *close_elem = 1;
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+LY_ERR
+lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
+ struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_json_ctx *lydctx;
+ enum LYJSON_PARSER_STATUS status;
+ uint32_t i, int_opts = 0, close_elem = 0;
+ ly_bool parsed_data_nodes = 0;
+
+ assert(ctx && in && lydctx_p);
+ assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+ assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
+
+ assert((data_type == LYD_TYPE_RPC_RESTCONF) || (data_type == LYD_TYPE_NOTIF_RESTCONF) ||
+ (data_type == LYD_TYPE_REPLY_RESTCONF));
+ assert(!(parse_opts & LYD_PARSE_SUBTREE));
+
+ /* init context */
+ rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
+ LY_CHECK_GOTO(rc, cleanup);
+ lydctx->ext = ext;
+
+ switch (data_type) {
+ case LYD_TYPE_RPC_RESTCONF:
+ assert(parent);
+
+ /* parse "input" */
+ rc = lydjson_envelope(lydctx->jsonctx, "input", lyd_owner_module(parent)->name, 0, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+ close_elem = 1;
+ break;
+ case LYD_TYPE_NOTIF_RESTCONF:
+ assert(!parent);
+ rc = lydjson_env_restconf_notif(lydctx->jsonctx, envp, &int_opts, &close_elem);
+ LY_CHECK_GOTO(rc, cleanup);
+ break;
+ case LYD_TYPE_REPLY_RESTCONF:
+ assert(parent);
+
+ /* parse "output" */
+ rc = lydjson_envelope(lydctx->jsonctx, "output", lyd_owner_module(parent)->name, 0, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+ close_elem = 1;
+ break;
+ default:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+ lydctx->int_opts = int_opts;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
+ /* read subtree(s) */
+ do {
+ r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ parsed_data_nodes = 1;
+
+ status = lyjson_ctx_status(lydctx->jsonctx);
+
+ if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+ break;
+ }
+ } while (status == LYJSON_OBJECT_NEXT);
+ assert((status == LYJSON_END) || (status == LYJSON_OBJECT_CLOSED));
+
+ /* close all opened elements */
+ for (i = 0; i < close_elem; ++i) {
+ if (status != LYJSON_OBJECT_CLOSED) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED),
+ lyjson_token2str(status));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] &&
+ (lyjson_ctx_status(lydctx->jsonctx) != LYJSON_OBJECT_CLOSED)) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+ /* finish linking metadata */
+ r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ if (!parsed_data_nodes) {
+ /* no data nodes were parsed */
+ lydctx->op_node = NULL;
+ }
+
+cleanup:
+ /* there should be no unres stored if validation should be skipped */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+ !lydctx->node_when.count));
+
+ if (rc) {
+ lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+ } else {
+ *lydctx_p = (struct lyd_ctx *)lydctx;
+
+ /* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */
+ lyjson_ctx_free(lydctx->jsonctx);
+ lydctx->jsonctx = NULL;
+ }
+ return rc;
+}
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 0f86a68..54e6c8b 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -1278,57 +1278,6 @@
}
/**
- * @brief Validate eventTime date-and-time value.
- *
- * @param[in] node Opaque eventTime node.
- * @return LY_SUCCESS on success.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lydxml_env_netconf_eventtime_validate(const struct lyd_node *node)
-{
- LY_ERR rc = LY_SUCCESS;
- struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
- struct lysc_ctx cctx;
- const struct lys_module *mod;
- LY_ARRAY_COUNT_TYPE u;
- struct ly_err_item *err = NULL;
- struct lysp_type *type_p = NULL;
- struct lysc_pattern **patterns = NULL;
- const char *value;
-
- LYSC_CTX_INIT_CTX(cctx, ctx);
-
- /* get date-and-time parsed type */
- mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
- assert(mod);
- LY_ARRAY_FOR(mod->parsed->typedefs, u) {
- if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
- type_p = &mod->parsed->typedefs[u].type;
- break;
- }
- }
- assert(type_p);
-
- /* compile patterns */
- assert(type_p->patterns);
- LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
-
- /* validate */
- value = lyd_get_value(node);
- rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
-
-cleanup:
- FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
- if (rc && err) {
- LOGVAL_ERRITEM(ctx, err);
- ly_err_free(err);
- LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
- }
- return rc;
-}
-
-/**
* @brief Parse all expected non-data XML elements of a NETCONF notification message.
*
* @param[in] xmlctx XML parser context.
@@ -1369,7 +1318,7 @@
lyd_insert_node(*envp, NULL, child, 0);
/* validate value */
- r = lydxml_env_netconf_eventtime_validate(child);
+ r = lyd_parser_notif_eventtime_validate(child);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* finish child parsing */
@@ -1868,9 +1817,6 @@
assert(ctx && in && lydctx_p);
assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
-
- assert((data_type == LYD_TYPE_RPC_NETCONF) || (data_type == LYD_TYPE_NOTIF_NETCONF) ||
- (data_type == LYD_TYPE_REPLY_NETCONF));
assert(!(parse_opts & LYD_PARSE_SUBTREE));
/* init context */
@@ -1907,6 +1853,32 @@
}
LY_CHECK_GOTO(rc, cleanup);
break;
+ case LYD_TYPE_RPC_RESTCONF:
+ assert(parent);
+
+ /* parse "input" */
+ rc = lydxml_envelope(lydctx->xmlctx, "input", lyd_owner_module(parent)->ns, 0, envp);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"input\" object or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+ close_elem = 1;
+ break;
+ case LYD_TYPE_REPLY_RESTCONF:
+ assert(parent);
+
+ /* parse "output" */
+ rc = lydxml_envelope(lydctx->xmlctx, "output", lyd_owner_module(parent)->ns, 0, envp);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"output\" object or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+ close_elem = 1;
+ break;
default:
LOGINT(ctx);
rc = LY_EINT;
diff --git a/src/tree_data.c b/src/tree_data.c
index 45f944b..d38acf2 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -261,28 +261,7 @@
*
* At least one of @p parent, @p tree, or @p op must always be set.
*
- * Specific @p data_type values have different parameter meaning as follows:
- * - ::LYD_TYPE_RPC_NETCONF:
- * - @p parent - must be NULL, the whole RPC is expected;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation;
- *
- * - ::LYD_TYPE_NOTIF_NETCONF:
- * - @p parent - must be NULL, the whole notification is expected;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
- *
- * - ::LYD_TYPE_REPLY_NETCONF:
- * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be NULL, the reply is appended to the RPC;
- * Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
+ * Specific @p data_type values have different parameter meaning as mentioned for ::lyd_parse_op().
*
* @param[in] ctx libyang context.
* @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed.
@@ -304,6 +283,7 @@
struct ly_set parsed = {0};
struct lyd_node *first = NULL, *envp = NULL;
uint32_t i, parse_opts, val_opts, int_opts = 0;
+ ly_bool proto_msg = 0;
if (!ctx) {
ctx = LYD_CTX(parent);
@@ -325,20 +305,51 @@
val_opts = 0;
switch (data_type) {
-
- /* special XML NETCONF data types */
case LYD_TYPE_RPC_NETCONF:
case LYD_TYPE_NOTIF_NETCONF:
LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL);
- /* fallthrough */
+ proto_msg = 1;
+ break;
case LYD_TYPE_REPLY_NETCONF:
- if (data_type == LYD_TYPE_REPLY_NETCONF) {
- LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op,
- LY_EINVAL);
- }
+ LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION),
+ tree, !op, LY_EINVAL);
+ proto_msg = 1;
+ break;
+ case LYD_TYPE_RPC_RESTCONF:
+ case LYD_TYPE_REPLY_RESTCONF:
+ LY_CHECK_ARG_RET(ctx, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op, LY_EINVAL);
+ proto_msg = 1;
+ break;
+ case LYD_TYPE_NOTIF_RESTCONF:
+ LY_CHECK_ARG_RET(ctx, format == LYD_JSON, !parent, tree, op, LY_EINVAL);
+ proto_msg = 1;
+ break;
- /* parse the NETCONF message */
- rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ /* set internal opts */
+ case LYD_TYPE_RPC_YANG:
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_NOTIF_YANG:
+ int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_REPLY_YANG:
+ int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_DATA_YANG:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+ /* parse a full protocol message */
+ if (proto_msg) {
+ if (format == LYD_XML) {
+ /* parse the NETCONF (or RESTCONF XML) message */
+ rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ } else {
+ /* parse the RESTCONF message */
+ rc = lyd_parse_json_restconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ }
if (rc) {
if (envp) {
/* special situation when the envelopes were parsed successfully */
@@ -358,21 +369,6 @@
*op = lydctx->op_node;
}
goto cleanup;
-
- /* set internal opts */
- case LYD_TYPE_RPC_YANG:
- int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_NOTIF_YANG:
- int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_REPLY_YANG:
- int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_DATA_YANG:
- LOGINT(ctx);
- rc = LY_EINT;
- goto cleanup;
}
/* parse the data */
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index 7c142f7..d2c40e6 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -777,6 +777,82 @@
/* TODO */
}
+static void
+test_restconf_rpc(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *envp;
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL)));
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree));
+
+ data = "{\"ietf-netconf-nmda:input\":{"
+ "\"datastore\":\"ietf-datastores:running\","
+ "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}},"
+ "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"
+ "}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_JSON, LYD_TYPE_RPC_RESTCONF, &envp, NULL));
+ ly_in_free(in, 0);
+
+ /* the same just connected to the edit-data RPC */
+ data = "{\"ietf-netconf-nmda:edit-data\":{"
+ "\"datastore\":\"ietf-datastores:running\","
+ "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}},"
+ "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"
+ "}}";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+}
+
+static void
+test_restconf_notification(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *ntf;
+
+ data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\",\"a:c\":{\"n1\":{\"nl\":\"value\"}}}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf));
+ ly_in_free(in, 0);
+
+ /* envelopes separately */
+ data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\"}}";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+ /* notification with the parent node */
+ data = "{\"a:c\":{\"n1\":{\"nl\":\"value\"}}}";
+ CHECK_LYD_STRING(lyd_parent(ntf), LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+ lyd_free_all(tree);
+ lyd_free_all(ntf);
+}
+
+static void
+test_restconf_reply(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *envp;
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree));
+
+ data = "{\"a:output\":{\"al\":25}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_JSON, LYD_TYPE_REPLY_RESTCONF, &envp, NULL));
+ ly_in_free(in, 0);
+
+ /* connected to the RPC with the parent */
+ data = "{\"a:c\":{\"act\":{\"al\":25}}}";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+}
+
int
main(void)
{
@@ -792,6 +868,9 @@
UTEST(test_action, setup),
UTEST(test_notification, setup),
UTEST(test_reply, setup),
+ UTEST(test_restconf_rpc, setup),
+ UTEST(test_restconf_notification, setup),
+ UTEST(test_restconf_reply, setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index d63fafa..04397ae 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -789,6 +789,64 @@
}
static void
+test_restconf_rpc(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *envp;
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL)));
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree));
+
+ data = "<input xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">"
+ "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>"
+ "<config>"
+ "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>"
+ "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">"
+ "<a>val_a</a><b>val_b</b><c>val_c</c>"
+ "</l1>"
+ "</config></input>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_XML, LYD_TYPE_RPC_RESTCONF, &envp, NULL));
+ ly_in_free(in, 0);
+
+ /* the same just connected to the edit-data RPC */
+ data = "<edit-data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">"
+ "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>"
+ "<config>"
+ "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>"
+ "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">"
+ "<a>val_a</a><b>val_b</b><c>val_c</c>"
+ "</l1>"
+ "</config></edit-data>";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+}
+
+static void
+test_restconf_reply(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *envp;
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree));
+
+ data = "<output xmlns=\"urn:tests:a\"><al>25</al></output>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_XML, LYD_TYPE_REPLY_RESTCONF, &envp, NULL));
+ ly_in_free(in, 0);
+
+ /* connected to the RPC with the parent */
+ data = "<c xmlns=\"urn:tests:a\"><act><al>25</al></act></c>";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+}
+
+static void
test_filter_attributes(void **state)
{
const char *data;
@@ -879,6 +937,8 @@
UTEST(test_netconf_rpc, setup),
UTEST(test_netconf_action, setup),
UTEST(test_netconf_reply_or_notification, setup),
+ UTEST(test_restconf_rpc, setup),
+ UTEST(test_restconf_reply, setup),
UTEST(test_filter_attributes, setup),
UTEST(test_data_skip, setup),
};