Skip to content

Commit

Permalink
Allow parsing standalone notifications in Context::parseOp
Browse files Browse the repository at this point in the history
Even when the libyang docs is not explicit on that, lyd_parse_op is used
for parsing standalone notifications too.

For such case, the lyd_node* tree and op are the same, or, they point
into the nodes in the same tree. So we should not wrap the pointers
separately (this would result in a heap-after-use).

Change-Id: Ib94114193a1a9073d9a4c6d15522eb41ba9c425d
  • Loading branch information
peckato1 committed Jun 12, 2024
1 parent c5ecde0 commit 92049a8
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 42 deletions.
20 changes: 14 additions & 6 deletions src/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ std::optional<DataNode> Context::parseData(
*
* - a NETCONF RPC,
* - a NETCONF notification,
* - a RESTCONF notification.
* - a RESTCONF notification,
* - a YANG notification.
*
* Parsing any of these requires just the schema (which is available through the Context), and the textual payload.
* All the other information are encoded in the textual payload as per the standard.
Expand All @@ -211,14 +212,21 @@ ParsedOp Context::parseOp(const std::string& input, const DataFormat format, con
switch (opType) {
case OperationType::RpcNetconf:
case OperationType::NotificationNetconf:
case OperationType::NotificationRestconf: {
case OperationType::NotificationRestconf:
case OperationType::NotificationYang: {
lyd_node* op = nullptr;
lyd_node* tree = nullptr;
auto err = lyd_parse_op(m_ctx.get(), nullptr, in.get(), utils::toLydFormat(format), utils::toOpType(opType), &tree, &op);
ParsedOp res {
.tree = tree ? std::optional{libyang::wrapRawNode(tree)} : std::nullopt,
.op = op ? std::optional{libyang::wrapRawNode(op)} : std::nullopt
};

ParsedOp res;
res.tree = tree ? std::optional{libyang::wrapRawNode(tree)} : std::nullopt;

if (opType == OperationType::NotificationYang) {
res.op = op && tree ? std::optional{DataNode(op, res.tree->m_refs)} : std::nullopt;
} else {
res.op = op ? std::optional{libyang::wrapRawNode(op)} : std::nullopt;
}

throwIfError(err, "Can't parse a standalone rpc/action/notification into operation data tree");
return res;
}
Expand Down
144 changes: 108 additions & 36 deletions tests/data_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2062,48 +2062,117 @@ TEST_CASE("Data Node manipulation")
ctx.loadModule("ietf-netconf-nmda");

DOCTEST_SUBCASE("notifications") {
std::string payload;
auto opType = libyang::OperationType::DataYang;
DOCTEST_SUBCASE("restconf/netconf") {
std::string payload;
auto opType = libyang::OperationType::DataYang;

DOCTEST_SUBCASE("RESTCONF JSON") {
payload = R"(
{
"ietf-restconf:notification" : {
"eventTime" : "2013-12-21T00:01:00Z",
"example-schema:event" : {
"event-class" : "fault"
DOCTEST_SUBCASE("RESTCONF JSON") {
payload = R"(
{
"ietf-restconf:notification" : {
"eventTime" : "2013-12-21T00:01:00Z",
"example-schema:event" : {
"event-class" : "fault"
}
}
}
}
)";
opType = libyang::OperationType::NotificationRestconf;
}
)";
opType = libyang::OperationType::NotificationRestconf;
}

DOCTEST_SUBCASE("NETCONF XML") {
payload = R"(
<notification
xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
<eventTime>2013-12-21T00:01:00Z</eventTime>
<event xmlns="http://example.com/coze">
<event-class>fault</event-class>
</event>
</notification>
)";
opType = libyang::OperationType::NotificationNetconf;

DOCTEST_SUBCASE("NETCONF XML") {
payload = R"(
<notification
xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
<eventTime>2013-12-21T00:01:00Z</eventTime>
<event xmlns="http://example.com/coze">
<event-class>fault</event-class>
</event>
</notification>
)";
opType = libyang::OperationType::NotificationNetconf;
}

auto notif = ctx.parseOp(payload, dataTypeFor(payload), opType);
REQUIRE(notif.tree);
REQUIRE(notif.tree->path() == "/notification");
auto node = notif.tree->child();
REQUIRE(node);
REQUIRE(node->path() == "/notification/eventTime");
REQUIRE(node->asOpaque().value() == "2013-12-21T00:01:00Z");

REQUIRE(notif.op);
node = notif.op->findPath("/example-schema:event/event-class");
REQUIRE(!!node);
REQUIRE(std::visit(libyang::ValuePrinter{}, node->asTerm().value()) == "fault");
}

auto notif = ctx.parseOp(payload, dataTypeFor(payload), opType);
REQUIRE(notif.tree);
REQUIRE(notif.tree->path() == "/notification");
auto node = notif.tree->child();
REQUIRE(node);
REQUIRE(node->path() == "/notification/eventTime");
REQUIRE(node->asOpaque().value() == "2013-12-21T00:01:00Z");
DOCTEST_SUBCASE("yang")
{
std::string payload;

REQUIRE(notif.op);
node = notif.op->findPath("/example-schema:event/event-class");
REQUIRE(!!node);
REQUIRE(std::visit(libyang::ValuePrinter{}, node->asTerm().value()) == "fault");
DOCTEST_SUBCASE("top-level")
{
DOCTEST_SUBCASE("json")
{
payload = R"({
"example-schema:event" : {
"event-class" : "fault"
}
})";
}
DOCTEST_SUBCASE("xml")
{
payload = R"(
<event xmlns="http://example.com/coze">
<event-class>fault</event-class>
</event>
)";
}
auto notif = ctx.parseOp(payload, dataTypeFor(payload), libyang::OperationType::NotificationYang);
REQUIRE(notif.tree);
REQUIRE(notif.op);
REQUIRE(notif.op == notif.tree);
REQUIRE(notif.tree->path() == "/example-schema:event");
auto node = notif.op->findPath("/example-schema:event/event-class");
REQUIRE(!!node);
REQUIRE(std::visit(libyang::ValuePrinter{}, node->asTerm().value()) == "fault");
}

DOCTEST_SUBCASE("nested")
{
DOCTEST_SUBCASE("json")
{
payload = R"({
"example-schema:person" : [{
"name": "John",
"event": {
"description" : "fault"
}
}]
})";
}
DOCTEST_SUBCASE("xml")
{
payload = R"(
<person xmlns="http://example.com/coze">
<name>John</name>
<event>
<description>fault</description>
</event>
</person>
)";
}
auto notif = ctx.parseOp(payload, dataTypeFor(payload), libyang::OperationType::NotificationYang);
REQUIRE(notif.tree);
REQUIRE(notif.op);
REQUIRE(notif.op != notif.tree);
REQUIRE(notif.tree->path() == "/example-schema:person[name='John']");
auto node = notif.op->findPath("/example-schema:person[name='John']/event/description");
REQUIRE(!!node);
REQUIRE(std::visit(libyang::ValuePrinter{}, node->asTerm().value()) == "fault");
}
}
}

DOCTEST_SUBCASE("invalid notification") {
Expand All @@ -2116,6 +2185,9 @@ TEST_CASE("Data Node manipulation")
REQUIRE_THROWS_WITH_AS(ctx.parseOp("", libyang::DataFormat::XML, libyang::OperationType::NotificationNetconf),
"Can't parse a standalone rpc/action/notification into operation data tree: LY_ENOT", libyang::Error);

REQUIRE_THROWS_WITH_AS(ctx.parseOp("asd", libyang::DataFormat::XML, libyang::OperationType::NotificationYang),
"Can't parse a standalone rpc/action/notification into operation data tree: LY_EVALID", libyang::Error);

/* libyang::setLogOptions(libyang::LogOptions::Log | libyang::LogOptions::Store); */
REQUIRE_THROWS_WITH_AS(ctx.parseOp(R"(
{
Expand Down
6 changes: 6 additions & 0 deletions tests/example_schema.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ module example-schema {
leaf name {
type string;
}
notification event {
leaf description {
type string;
}
}
}
leaf bossPerson {
Expand Down

0 comments on commit 92049a8

Please sign in to comment.