REST: Add method return support for arrays/dicts
Add support for returning arrays and dictionaries
from methods.
Note that a dictionary can only be seen inside of
an array, and every key/value pair is in its own
sd_bus_message container.
Change-Id: I4f8ff671f7c4403d83443482e7db0487bdc03ff1
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index b044488..a9a6a10 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -814,6 +814,147 @@
}
int convertDBusToJSON(const std::string &returnType,
+ sdbusplus::message::message &m, nlohmann::json &response);
+
+int readDictEntryFromMessage(const std::string &typeCode,
+ sdbusplus::message::message &m,
+ nlohmann::json &object)
+{
+ std::vector<std::string> types = dbusArgSplit(typeCode);
+ if (types.size() != 2)
+ {
+ BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
+ << types.size();
+ return -1;
+ }
+
+ int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
+ typeCode.c_str());
+ if (r < 0)
+ {
+ BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
+ return r;
+ }
+
+ nlohmann::json key;
+ r = convertDBusToJSON(types[0], m, key);
+ if (r < 0)
+ {
+ return r;
+ }
+
+ const std::string *keyPtr = key.get_ptr<const std::string *>();
+ if (keyPtr == nullptr)
+ {
+ // json doesn't support non-string keys. If we hit this condition,
+ // convert the result to a string so we can proceed
+ key = key.dump();
+ keyPtr = key.get_ptr<const std::string *>();
+ // in theory this can't fail now, but lets be paranoid about it anyway
+ if (keyPtr == nullptr)
+ {
+ return -1;
+ }
+ }
+ nlohmann::json &value = object[*keyPtr];
+
+ r = convertDBusToJSON(types[1], m, value);
+ if (r < 0)
+ {
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(m.get());
+ if (r < 0)
+ {
+ BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
+ return r;
+ }
+
+ return 0;
+}
+
+int readArrayFromMessage(const std::string &typeCode,
+ sdbusplus::message::message &m, nlohmann::json &data)
+{
+ if (typeCode.size() < 2)
+ {
+ BMCWEB_LOG_ERROR << "Type code " << typeCode
+ << " too small for an array";
+ return -1;
+ }
+
+ std::string containedType = typeCode.substr(1);
+
+ int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
+ containedType.c_str());
+ if (r < 0)
+ {
+ BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
+ << r;
+ return r;
+ }
+
+ bool dict = boost::starts_with(containedType, "{") &&
+ boost::ends_with(containedType, "}");
+
+ if (dict)
+ {
+ // Remove the { }
+ containedType = containedType.substr(1, containedType.size() - 2);
+ data = nlohmann::json::object();
+ }
+ else
+ {
+ data = nlohmann::json::array();
+ }
+
+ while (true)
+ {
+ r = sd_bus_message_at_end(m.get(), false);
+ if (r < 0)
+ {
+ BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
+ return r;
+ }
+
+ if (r > 0)
+ {
+ break;
+ }
+
+ // Dictionaries are only ever seen in an array
+ if (dict)
+ {
+ r = readDictEntryFromMessage(containedType, m, data);
+ if (r < 0)
+ {
+ return r;
+ }
+ }
+ else
+ {
+ data.push_back(nlohmann::json());
+
+ r = convertDBusToJSON(containedType, m, data.back());
+ if (r < 0)
+ {
+ return r;
+ }
+ }
+ }
+
+ r = sd_bus_message_exit_container(m.get());
+ if (r < 0)
+ {
+ BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
+ return r;
+ }
+
+ return 0;
+}
+
+int convertDBusToJSON(const std::string &returnType,
sdbusplus::message::message &m, nlohmann::json &response)
{
int r = 0;
@@ -934,9 +1075,17 @@
return r;
}
}
+ else if (boost::starts_with(typeCode, "a"))
+ {
+ r = readArrayFromMessage(typeCode, m, thisElement);
+ if (r < 0)
+ {
+ return r;
+ }
+ }
else
{
- // TODO: add array, dict, variant support
+ // TODO: add struct, variant support
BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
return -2;
}