From 3b436a7172c63a25b15c205594a83ef81da1ee19 Mon Sep 17 00:00:00 2001 From: Dmitrii Date: Thu, 10 Jul 2025 13:43:28 +0300 Subject: [PATCH 1/3] Fix parsing of gNMI response without namespaces See https://github.com/PANTHEONtech/lighty/issues/2197 --- .../GetResponseToNormalizedNodeCodec.java | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java b/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java index 25b5c762b7..ecaf6bccd3 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java @@ -10,6 +10,7 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParser; import gnmi.Gnmi; import gnmi.Gnmi.Update; @@ -84,7 +85,12 @@ private NormalizedNode updateToNormalizedNode(final Update update, final YangIns Check if response is rooted deeper than requested, if yes, wrap it so it is rooted at the same level as identifier last path arg points to. */ - if (!identifier.isEmpty() && isResponseJsonDeeperThanRequested(identifier, responseJson)) { + if (!identifier.isEmpty()) { + + final String lastPathArgName = identifier.getLastPathArgument().getNodeType().getLocalName(); + JsonElement jsonObject = JsonParser.parseString(responseJson); + + if (isResponseJsonDeeperThanRequested(lastPathArgName, jsonObject)) { final QName lastName = identifier.getLastPathArgument().getNodeType(); final Module moduleByQName = DataConverter.findModuleByQName(lastName, schemaContextProvider.getSchemaContext()) @@ -101,8 +107,13 @@ private NormalizedNode updateToNormalizedNode(final Update update, final YangIns } else { responseJson = JsonUtils.wrapJsonWithObject(responseJson, wrapWith, gson); } + } else if (isResponseJsonWithoutNamespace(jsonObject)) { + // Add missing namespace to the response from the request. + JsonElement responseJsonWithNamespace = addNamespaceToResponseJson(jsonObject, identifier, schemaContextProvider); + responseJson = responseJsonWithNamespace.toString(); } - return resolveJsonResponse(identifier, responseJson); + } + return resolveJsonResponse(identifier, responseJson); /* In the case of primitive values, only the value is present in response. Since json parser works only with object, always wrap the value (wrapPrimitive()). @@ -133,10 +144,8 @@ Since json parser works only with object, always wrap the value (wrapPrimitive() } } - private static boolean isResponseJsonDeeperThanRequested(final YangInstanceIdentifier identifier, - final String responseJson) { - final String lastPathArgName = identifier.getLastPathArgument().getNodeType().getLocalName(); - final JsonElement jsonObject = JsonParser.parseString(responseJson); + private static boolean isResponseJsonDeeperThanRequested(final String lastPathArgName, + final JsonElement jsonObject) { if (!jsonObject.isJsonObject()) { return true; } @@ -154,6 +163,34 @@ private static boolean isResponseJsonDeeperThanRequested(final YangInstanceIdent return true; } + private static boolean isResponseJsonWithoutNamespace(JsonElement jsonElement) { + Map.Entry firstElement = jsonElement.getAsJsonObject().entrySet().iterator().next(); + if (firstElement.getKey().contains(":")) { + return false; + } else { + return true; + } + } + + private static JsonElement addNamespaceToResponseJson(JsonElement jsonElement, + final YangInstanceIdentifier identifier, + final SchemaContextProvider schemaContextProvider) + throws GnmiCodecException { + final QName lastName = identifier.getLastPathArgument().getNodeType(); + final Module moduleByQName = + DataConverter.findModuleByQName(lastName, schemaContextProvider.getSchemaContext()) + .orElseThrow(() -> new GnmiCodecException( + String.format("Unable to find module of node %s", lastName))); + + final String moduleNameWithNamespace = String.format("%s:%s", moduleByQName.getName(), + lastName.getLocalName()); + + Map.Entry entry = jsonElement.getAsJsonObject().entrySet().iterator().next(); + JsonObject jsonObject = new JsonObject(); + jsonObject.add(moduleNameWithNamespace, entry.getValue()); + return jsonObject; + } + @SuppressWarnings("IllegalCatch") private NormalizedNode resolveJsonResponse(YangInstanceIdentifier identifier, String inputJson) throws GnmiCodecException { From ea3ec7301e953a123a880bbb8ed14c2867f139c1 Mon Sep 17 00:00:00 2001 From: Dmitrii Date: Thu, 10 Jul 2025 15:45:17 +0300 Subject: [PATCH 2/3] Fix code style in GetResponseToNormalizedNodeCodec --- .../GetResponseToNormalizedNodeCodec.java | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java b/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java index ecaf6bccd3..dbede792d8 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java @@ -86,34 +86,33 @@ private NormalizedNode updateToNormalizedNode(final Update update, final YangIns the same level as identifier last path arg points to. */ if (!identifier.isEmpty()) { - final String lastPathArgName = identifier.getLastPathArgument().getNodeType().getLocalName(); JsonElement jsonObject = JsonParser.parseString(responseJson); - if (isResponseJsonDeeperThanRequested(lastPathArgName, jsonObject)) { - final QName lastName = identifier.getLastPathArgument().getNodeType(); - final Module moduleByQName = - DataConverter.findModuleByQName(lastName, schemaContextProvider.getSchemaContext()) - .orElseThrow(() -> new GnmiCodecException( - String.format("Unable to find module of node %s", lastName))); + final QName lastName = identifier.getLastPathArgument().getNodeType(); + final Module moduleByQName = + DataConverter.findModuleByQName(lastName, schemaContextProvider.getSchemaContext()) + .orElseThrow(() -> new GnmiCodecException( + String.format("Unable to find module of node %s", lastName))); - final String wrapWith = String.format("%s:%s", moduleByQName.getName(), - lastName.getLocalName()); - if (identifier.getLastPathArgument() instanceof NodeIdentifierWithPredicates) { - final NodeIdentifierWithPredicates lastPathArgument - = (NodeIdentifierWithPredicates) identifier.getLastPathArgument(); - responseJson = JsonUtils.wrapJsonWithArray(responseJson, wrapWith, gson, lastPathArgument, - schemaContextProvider.getSchemaContext()); - } else { - responseJson = JsonUtils.wrapJsonWithObject(responseJson, wrapWith, gson); + final String wrapWith = String.format("%s:%s", moduleByQName.getName(), + lastName.getLocalName()); + if (identifier.getLastPathArgument() instanceof NodeIdentifierWithPredicates) { + final NodeIdentifierWithPredicates lastPathArgument + = (NodeIdentifierWithPredicates) identifier.getLastPathArgument(); + responseJson = JsonUtils.wrapJsonWithArray(responseJson, wrapWith, gson, lastPathArgument, + schemaContextProvider.getSchemaContext()); + } else { + responseJson = JsonUtils.wrapJsonWithObject(responseJson, wrapWith, gson); + } + } else if (isResponseJsonWithoutNamespace(jsonObject)) { + // Add missing namespace to the response from the request. + JsonElement responseJsonWithNamespace = addNamespaceToResponseJson(jsonObject, identifier, + schemaContextProvider); + responseJson = responseJsonWithNamespace.toString(); } - } else if (isResponseJsonWithoutNamespace(jsonObject)) { - // Add missing namespace to the response from the request. - JsonElement responseJsonWithNamespace = addNamespaceToResponseJson(jsonObject, identifier, schemaContextProvider); - responseJson = responseJsonWithNamespace.toString(); } - } - return resolveJsonResponse(identifier, responseJson); + return resolveJsonResponse(identifier, responseJson); /* In the case of primitive values, only the value is present in response. Since json parser works only with object, always wrap the value (wrapPrimitive()). @@ -172,8 +171,8 @@ private static boolean isResponseJsonWithoutNamespace(JsonElement jsonElement) { } } - private static JsonElement addNamespaceToResponseJson(JsonElement jsonElement, - final YangInstanceIdentifier identifier, + private static JsonElement addNamespaceToResponseJson(JsonElement jsonElement, + final YangInstanceIdentifier identifier, final SchemaContextProvider schemaContextProvider) throws GnmiCodecException { final QName lastName = identifier.getLastPathArgument().getNodeType(); @@ -184,7 +183,6 @@ private static JsonElement addNamespaceToResponseJson(JsonElement jsonElement, final String moduleNameWithNamespace = String.format("%s:%s", moduleByQName.getName(), lastName.getLocalName()); - Map.Entry entry = jsonElement.getAsJsonObject().entrySet().iterator().next(); JsonObject jsonObject = new JsonObject(); jsonObject.add(moduleNameWithNamespace, entry.getValue()); From 7f73c102efcb96faec44d498b4dd9adb156c2fef Mon Sep 17 00:00:00 2001 From: Dmitrii Date: Tue, 22 Jul 2025 17:22:23 +0300 Subject: [PATCH 3/3] Apply code review in PR 2250 --- .../codecs/GetResponseToNormalizedNodeCodec.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java b/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java index dbede792d8..aa9f37e7ce 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java @@ -162,8 +162,8 @@ private static boolean isResponseJsonDeeperThanRequested(final String lastPathAr return true; } - private static boolean isResponseJsonWithoutNamespace(JsonElement jsonElement) { - Map.Entry firstElement = jsonElement.getAsJsonObject().entrySet().iterator().next(); + private static boolean isResponseJsonWithoutNamespace(final JsonElement jsonElement) { + final Map.Entry firstElement = jsonElement.getAsJsonObject().entrySet().iterator().next(); if (firstElement.getKey().contains(":")) { return false; } else { @@ -171,7 +171,7 @@ private static boolean isResponseJsonWithoutNamespace(JsonElement jsonElement) { } } - private static JsonElement addNamespaceToResponseJson(JsonElement jsonElement, + private static JsonElement addNamespaceToResponseJson(final JsonElement jsonElement, final YangInstanceIdentifier identifier, final SchemaContextProvider schemaContextProvider) throws GnmiCodecException { @@ -183,7 +183,7 @@ private static JsonElement addNamespaceToResponseJson(JsonElement jsonElement, final String moduleNameWithNamespace = String.format("%s:%s", moduleByQName.getName(), lastName.getLocalName()); - Map.Entry entry = jsonElement.getAsJsonObject().entrySet().iterator().next(); + final Map.Entry entry = jsonElement.getAsJsonObject().entrySet().iterator().next(); JsonObject jsonObject = new JsonObject(); jsonObject.add(moduleNameWithNamespace, entry.getValue()); return jsonObject;