mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-06 23:43:20 +02:00
357 lines
11 KiB
Diff
357 lines
11 KiB
Diff
From 22442d322bda3d83457dd0f1da45d95a10e93081 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
|
Date: Thu, 13 Mar 2025 17:32:59 +0100
|
|
Subject: [PATCH 30/44] Always print empty containers in user-defined content
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
Organization: Wires
|
|
|
|
There's some subtle interaction between depth-limited printing, default
|
|
values, and empty non-presence containers. In order to prepare for an
|
|
upstream change in sysrepo which fixed depth-limited printing, let's
|
|
stick to "always" printing the empty non-presence containers.
|
|
|
|
(It actually isn't "always" because the data such as error reports,
|
|
patches, etc., are still printed using the default settings. But the
|
|
data which is subject to user-defined constraints is now consistent.)
|
|
|
|
Change-Id: Ie2d6ad2e851d667e7e7582fcd0ac75dabce2c05e
|
|
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
|
---
|
|
src/restconf/Server.cpp | 4 +-
|
|
src/restconf/utils/yang.cpp | 2 +-
|
|
src/sr/AllEvents.cpp | 2 +-
|
|
src/sr/OpticalEvents.cpp | 2 +-
|
|
tests/restconf-defaults.cpp | 12 +++++-
|
|
tests/restconf-nacm.cpp | 12 +++++-
|
|
tests/restconf-reading.cpp | 79 ++++++++++++++++++++++++++++++++++---
|
|
7 files changed, 98 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/src/restconf/Server.cpp b/src/restconf/Server.cpp
|
|
index 55e504a..bae3992 100644
|
|
--- a/src/restconf/Server.cpp
|
|
+++ b/src/restconf/Server.cpp
|
|
@@ -478,7 +478,7 @@ void processActionOrRPC(std::shared_ptr<RequestContext> requestCtx, const std::c
|
|
contentType(requestCtx->dataFormat.response),
|
|
CORS,
|
|
});
|
|
- requestCtx->res.end(*envelope->printStr(requestCtx->dataFormat.response, libyang::PrintFlags::WithSiblings));
|
|
+ requestCtx->res.end(*envelope->printStr(requestCtx->dataFormat.response, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont));
|
|
}
|
|
|
|
void processPost(std::shared_ptr<RequestContext> requestCtx, const std::chrono::milliseconds timeout)
|
|
@@ -768,7 +768,7 @@ libyang::PrintFlags libyangPrintFlags(const libyang::DataNode& dataNode, const s
|
|
} catch(const libyang::Error& e) {
|
|
}
|
|
|
|
- libyang::PrintFlags ret = libyang::PrintFlags::WithSiblings;
|
|
+ libyang::PrintFlags ret = libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont;
|
|
|
|
if (!withDefaults && node && (node->schema().nodeType() == libyang::NodeType::Leaf || node->schema().nodeType() == libyang::NodeType::Leaflist) && node->asTerm().isImplicitDefault()) {
|
|
return ret | libyang::PrintFlags::WithDefaultsAll;
|
|
diff --git a/src/restconf/utils/yang.cpp b/src/restconf/utils/yang.cpp
|
|
index 30661fc..8acb160 100644
|
|
--- a/src/restconf/utils/yang.cpp
|
|
+++ b/src/restconf/utils/yang.cpp
|
|
@@ -111,7 +111,7 @@ std::string as_restconf_notification(const libyang::Context& ctx, libyang::DataF
|
|
envelope->insertChild(*eventTime);
|
|
envelope->insertChild(notification);
|
|
|
|
- auto res = *envelope->printStr(dataFormat, libyang::PrintFlags::WithSiblings);
|
|
+ auto res = *envelope->printStr(dataFormat, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
|
|
|
|
// notification node comes from sysrepo and sysrepo will free this; if not unlinked then envelope destructor would try to free this as well
|
|
notification.unlink();
|
|
diff --git a/src/sr/AllEvents.cpp b/src/sr/AllEvents.cpp
|
|
index 6e2a21c..2aca4c7 100644
|
|
--- a/src/sr/AllEvents.cpp
|
|
+++ b/src/sr/AllEvents.cpp
|
|
@@ -109,7 +109,7 @@ sysrepo::ErrorCode AllEvents::onChange(sysrepo::Session session, const std::stri
|
|
break;
|
|
}
|
|
};
|
|
- auto json = *copy.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
|
|
+ auto json = *copy.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
|
|
spdlog::info("JSON: {}", json);
|
|
spdlog::warn("FULL JSON: {}",
|
|
*session.getData('/' + module + ":*")->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings));
|
|
diff --git a/src/sr/OpticalEvents.cpp b/src/sr/OpticalEvents.cpp
|
|
index 8c5feff..25906a8 100644
|
|
--- a/src/sr/OpticalEvents.cpp
|
|
+++ b/src/sr/OpticalEvents.cpp
|
|
@@ -15,7 +15,7 @@
|
|
namespace {
|
|
std::string dumpDataFrom(sysrepo::Session session, const std::string& module)
|
|
{
|
|
- return *session.getData('/' + module + ":*")->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
|
|
+ return *session.getData('/' + module + ":*")->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
|
|
}
|
|
}
|
|
|
|
diff --git a/tests/restconf-defaults.cpp b/tests/restconf-defaults.cpp
|
|
index d5c6004..89083f1 100644
|
|
--- a/tests/restconf-defaults.cpp
|
|
+++ b/tests/restconf-defaults.cpp
|
|
@@ -33,7 +33,11 @@ TEST_CASE("default handling")
|
|
|
|
// default value of /example:a/b/c/enabled is implicitly set so it should not be printed
|
|
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a/b/c", {}) == Response{200, jsonHeaders, R"({
|
|
-
|
|
+ "example:a": {
|
|
+ "b": {
|
|
+ "c": {}
|
|
+ }
|
|
+ }
|
|
}
|
|
)"});
|
|
|
|
@@ -88,7 +92,11 @@ TEST_CASE("default handling")
|
|
|
|
// default value is only there implicitly, so it should *not* be printed
|
|
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a/b/c", {}) == Response{200, jsonHeaders, R"({
|
|
-
|
|
+ "example:a": {
|
|
+ "b": {
|
|
+ "c": {}
|
|
+ }
|
|
+ }
|
|
}
|
|
)"});
|
|
}
|
|
diff --git a/tests/restconf-nacm.cpp b/tests/restconf-nacm.cpp
|
|
index 29d7723..51328f3 100644
|
|
--- a/tests/restconf-nacm.cpp
|
|
+++ b/tests/restconf-nacm.cpp
|
|
@@ -130,6 +130,9 @@ TEST_CASE("NACM")
|
|
"clock": {
|
|
"timezone-utc-offset": 2
|
|
},
|
|
+ "dns-resolver": {
|
|
+ "options": {}
|
|
+ },
|
|
"radius": {
|
|
"server": [
|
|
{
|
|
@@ -139,7 +142,8 @@ TEST_CASE("NACM")
|
|
"shared-secret": "shared-secret"
|
|
}
|
|
}
|
|
- ]
|
|
+ ],
|
|
+ "options": {}
|
|
}
|
|
}
|
|
}
|
|
@@ -207,6 +211,9 @@ TEST_CASE("NACM")
|
|
"clock": {
|
|
"timezone-utc-offset": 2
|
|
},
|
|
+ "dns-resolver": {
|
|
+ "options": {}
|
|
+ },
|
|
"radius": {
|
|
"server": [
|
|
{
|
|
@@ -216,7 +223,8 @@ TEST_CASE("NACM")
|
|
"shared-secret": "shared-secret"
|
|
}
|
|
}
|
|
- ]
|
|
+ ],
|
|
+ "options": {}
|
|
}
|
|
}
|
|
}
|
|
diff --git a/tests/restconf-reading.cpp b/tests/restconf-reading.cpp
|
|
index e709486..ffdb047 100644
|
|
--- a/tests/restconf-reading.cpp
|
|
+++ b/tests/restconf-reading.cpp
|
|
@@ -62,10 +62,22 @@ TEST_CASE("reading data")
|
|
// this relies on a NACM rule for anonymous access that filters out "a lot of stuff"
|
|
REQUIRE(get(RESTCONF_DATA_ROOT, {}) == Response{200, jsonHeaders, R"({
|
|
"example:top-level-leaf": "moo",
|
|
+ "example:tlc": {},
|
|
+ "example:a": {
|
|
+ "b": {
|
|
+ "c": {}
|
|
+ },
|
|
+ "b1": {},
|
|
+ "example-augment:b": {
|
|
+ "c": {}
|
|
+ }
|
|
+ },
|
|
+ "example:two-leafs": {},
|
|
"example:config-nonconfig": {
|
|
"config-node": "foo-config-true",
|
|
"nonconfig-node": "foo-config-false"
|
|
},
|
|
+ "example:ordered-lists": {},
|
|
"ietf-restconf-monitoring:restconf-state": {
|
|
"capabilities": {
|
|
"capability": [
|
|
@@ -107,10 +119,22 @@ TEST_CASE("reading data")
|
|
|
|
REQUIRE(get(RESTCONF_ROOT_DS("operational"), {}) == Response{200, jsonHeaders, R"({
|
|
"example:top-level-leaf": "moo",
|
|
+ "example:tlc": {},
|
|
+ "example:a": {
|
|
+ "b": {
|
|
+ "c": {}
|
|
+ },
|
|
+ "b1": {},
|
|
+ "example-augment:b": {
|
|
+ "c": {}
|
|
+ }
|
|
+ },
|
|
+ "example:two-leafs": {},
|
|
"example:config-nonconfig": {
|
|
"config-node": "foo-config-true",
|
|
"nonconfig-node": "foo-config-false"
|
|
},
|
|
+ "example:ordered-lists": {},
|
|
"ietf-restconf-monitoring:restconf-state": {
|
|
"capabilities": {
|
|
"capability": [
|
|
@@ -152,9 +176,21 @@ TEST_CASE("reading data")
|
|
|
|
REQUIRE(get(RESTCONF_ROOT_DS("running"), {}) == Response{200, jsonHeaders, R"({
|
|
"example:top-level-leaf": "moo",
|
|
+ "example:tlc": {},
|
|
+ "example:a": {
|
|
+ "b": {
|
|
+ "c": {}
|
|
+ },
|
|
+ "b1": {},
|
|
+ "example-augment:b": {
|
|
+ "c": {}
|
|
+ }
|
|
+ },
|
|
+ "example:two-leafs": {},
|
|
"example:config-nonconfig": {
|
|
"config-node": "foo-config-true"
|
|
- }
|
|
+ },
|
|
+ "example:ordered-lists": {}
|
|
}
|
|
)"});
|
|
}
|
|
@@ -213,7 +249,8 @@ TEST_CASE("reading data")
|
|
{
|
|
"name": "a"
|
|
}
|
|
- ]
|
|
+ ],
|
|
+ "options": {}
|
|
}
|
|
}
|
|
}
|
|
@@ -244,7 +281,8 @@ TEST_CASE("reading data")
|
|
"shared-secret": "shared-secret"
|
|
}
|
|
}
|
|
- ]
|
|
+ ],
|
|
+ "options": {}
|
|
}
|
|
}
|
|
}
|
|
@@ -714,6 +752,7 @@ TEST_CASE("reading data")
|
|
"enabled": true
|
|
}
|
|
},
|
|
+ "b1": {},
|
|
"example-augment:b": {
|
|
"c": {
|
|
"enabled": true
|
|
@@ -723,9 +762,18 @@ TEST_CASE("reading data")
|
|
}
|
|
)"});
|
|
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a?with-defaults=explicit", {}) == Response{200, jsonHeaders, R"({
|
|
-
|
|
+ "example:a": {
|
|
+ "b": {
|
|
+ "c": {}
|
|
+ },
|
|
+ "b1": {},
|
|
+ "example-augment:b": {
|
|
+ "c": {}
|
|
+ }
|
|
+ }
|
|
}
|
|
)"});
|
|
+ // FIXME: libyang is not really consistent in printing of NP-containers when trimming away the defaults...
|
|
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a?with-defaults=trim", {}) == Response{200, jsonHeaders, R"({
|
|
|
|
}
|
|
@@ -740,6 +788,7 @@ TEST_CASE("reading data")
|
|
}
|
|
}
|
|
},
|
|
+ "b1": {},
|
|
"example-augment:b": {
|
|
"c": {
|
|
"enabled": true,
|
|
@@ -766,6 +815,7 @@ TEST_CASE("reading data")
|
|
"enabled": true
|
|
}
|
|
},
|
|
+ "b1": {},
|
|
"example-augment:b": {
|
|
"c": {
|
|
"enabled": true
|
|
@@ -781,13 +831,24 @@ TEST_CASE("reading data")
|
|
"c": {
|
|
"enabled": true
|
|
}
|
|
+ },
|
|
+ "b1": {},
|
|
+ "example-augment:b": {
|
|
+ "c": {}
|
|
}
|
|
}
|
|
}
|
|
)"});
|
|
|
|
+ // FIXME: libyang is not consistent here:
|
|
+ // - /example:a/b/c NP-container *is* printed,
|
|
+ // - /example:a/example-augment:b/c NP-container is *not* printed
|
|
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a?with-defaults=trim", {}) == Response{200, jsonHeaders, R"({
|
|
-
|
|
+ "example:a": {
|
|
+ "b": {
|
|
+ "c": {}
|
|
+ }
|
|
+ }
|
|
}
|
|
)"});
|
|
|
|
@@ -801,6 +862,7 @@ TEST_CASE("reading data")
|
|
}
|
|
}
|
|
},
|
|
+ "b1": {},
|
|
"example-augment:b": {
|
|
"c": {
|
|
"enabled": true,
|
|
@@ -830,10 +892,15 @@ TEST_CASE("reading data")
|
|
)"});
|
|
|
|
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a/b/c/enabled?with-defaults=explicit", {}) == Response{200, jsonHeaders, R"({
|
|
-
|
|
+ "example:a": {
|
|
+ "b": {
|
|
+ "c": {}
|
|
+ }
|
|
+ }
|
|
}
|
|
)"});
|
|
|
|
+ // again, libyang is not 100% consistent in the `trim` mode
|
|
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a/b/c/enabled?with-defaults=trim", {}) == Response{200, jsonHeaders, R"({
|
|
|
|
}
|
|
--
|
|
2.43.0
|
|
|