diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-commons/src/main/java/io/lighty/modules/gnmi/commons/util/JsonUtils.java b/lighty-modules/lighty-gnmi/lighty-gnmi-commons/src/main/java/io/lighty/modules/gnmi/commons/util/JsonUtils.java index c35db5f12f..222a15c7dd 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-commons/src/main/java/io/lighty/modules/gnmi/commons/util/JsonUtils.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-commons/src/main/java/io/lighty/modules/gnmi/commons/util/JsonUtils.java @@ -18,8 +18,10 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.opendaylight.yangtools.yang.common.Decimal64; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; public final class JsonUtils { @@ -28,10 +30,26 @@ private JsonUtils() { } public static String wrapJsonWithArray(final String jsonString, final String wrapper, final Gson gson, - final NodeIdentifierWithPredicates predicates) { + final NodeIdentifierWithPredicates predicates, final EffectiveModelContext context) { final JsonObject innerJson = JsonParser.parseString(jsonString).getAsJsonObject(); for (final Entry key : predicates.entrySet()) { - innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(key.getValue())); + final var keyValue = key.getValue(); + // InstanceIdentifier for identityref is stored as a QName, value should be in format MODULE:IDENTITY_NAME. + if (keyValue instanceof QName qnameValue) { + final var module = context.findModule(qnameValue.getModule()).orElseThrow(); + final var value = String.format("%s:%s", module.getName(), qnameValue.getLocalName()); + innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(value)); + // Custom ODL Number types are not correctly parsed by Gson. + } else if (keyValue instanceof Number numberValue) { + if (numberValue instanceof Decimal64) { + innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(numberValue.doubleValue())); + } else { + innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(numberValue.longValue())); + } + // Other parse-able types. + } else { + innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(keyValue)); + } } final JsonObject result = new JsonObject(); diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-device-simulator/src/main/java/io/lighty/modules/gnmi/simulatordevice/gnmi/GnmiCrudService.java b/lighty-modules/lighty-gnmi/lighty-gnmi-device-simulator/src/main/java/io/lighty/modules/gnmi/simulatordevice/gnmi/GnmiCrudService.java index 82f6ba3ac3..a4b0be4ea3 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-device-simulator/src/main/java/io/lighty/modules/gnmi/simulatordevice/gnmi/GnmiCrudService.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-device-simulator/src/main/java/io/lighty/modules/gnmi/simulatordevice/gnmi/GnmiCrudService.java @@ -221,7 +221,7 @@ private Gnmi.UpdateResult processUpdateListNonSimpleValue(final Gnmi.Update upda String.format("%s:%s", module.getName(), Iterables.getLast(identifier.getPathArguments()).getNodeType().getLocalName()), gson, - lastPathArgument); + lastPathArgument, context); node = DataConverter.nodeFromJsonString(identifier, json, context); // In case of list entry, point to the list itself resultingIdentifier = identifier.getParent(); 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 16932060e4..0fb6fa419b 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 @@ -105,7 +105,8 @@ private NormalizedNode updateToNormalizedNode(final Update update, final YangIns if (identifier.getLastPathArgument() instanceof NodeIdentifierWithPredicates) { final NodeIdentifierWithPredicates lastPathArgument = (NodeIdentifierWithPredicates) identifier.getLastPathArgument(); - responseJson = JsonUtils.wrapJsonWithArray(responseJson, wrapWith, gson, lastPathArgument); + responseJson = JsonUtils.wrapJsonWithArray(responseJson, wrapWith, gson, lastPathArgument, + schemaContextProvider.getSchemaContext()); } else { responseJson = JsonUtils.wrapJsonWithObject(responseJson, wrapWith, gson); } diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-test/src/test/java/io/lighty/modules/gnmi/test/gnmi/SimulatorCrudTest.java b/lighty-modules/lighty-gnmi/lighty-gnmi-test/src/test/java/io/lighty/modules/gnmi/test/gnmi/SimulatorCrudTest.java index b706f1aa27..626e50964c 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-test/src/test/java/io/lighty/modules/gnmi/test/gnmi/SimulatorCrudTest.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-test/src/test/java/io/lighty/modules/gnmi/test/gnmi/SimulatorCrudTest.java @@ -324,6 +324,39 @@ public void setContainerWithMultipleListKeyInPathTest() throws Exception { assertEquals("UPDATE", setResponse.getResponse(1).getOp().toString()); } + @Test + public void setMultipleKeyListAsLastElementInPathTest() throws Exception { + final var multipleKeyPath = Gnmi.Path.newBuilder() + .addElem(Gnmi.PathElem.newBuilder() + .setName("gnmi-test-model:test-data") + .build()) + .addElem(Gnmi.PathElem.newBuilder() + .setName("multiple-key-list") + .putKey("number", "10") + .putKey("leafref-key", "15") + .putKey("identityref-key", "openconfig-aaa-types:SYSTEM_DEFINED_ROLES") + .putKey("union-key", "5") + .build()) + .build(); + final var innerContainerUpdate = Gnmi.Update.newBuilder() + .setPath(multipleKeyPath) + .setVal(Gnmi.TypedValue.newBuilder() + .setJsonIetfVal(ByteString.copyFromUtf8(""" + { + "inner-container": { + "inner-data": "data" + } + } + """)) + .build()) + .build(); + final var setRequest = Gnmi.SetRequest.newBuilder().addUpdate(innerContainerUpdate).build(); + LOG.info("Sending set request:\n{}", setRequest); + + final var setResponse = sessionProvider.getGnmiSession().set(setRequest).get(); + assertEquals("UPDATE", setResponse.getResponse(0).getOp().toString()); + } + @Test public void crudComplexValueTest() throws ExecutionException, InterruptedException, IOException, JSONException { final Gnmi.Path path = Gnmi.Path.newBuilder()