REST: If necessary, combine method response data
There is a possibility that a method may be implemented
by either different services or interfaces, and 1 or more
of them may return data.
In the unlikely case that is encountered, attempt to handle
that by first setting the final response data to the first
data back from a method, and then on future method responses
that return data:
* If the new and old responses are both dictionaries,
add the new keys/values to the original ones.
* If the new and old responses are both arrays,
add the new array elements to the original array.
* If the new data is of a different type than the
previous data, convert the overall response into
an array and add the new and original responses
as array elements.
Change-Id: I23edc3d9f8154aba1ba4276112cde6ecb4345fdf
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index 9f282a6..e310b1a 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -462,6 +462,7 @@
bool methodPassed = false;
bool methodFailed = false;
bool outputFailed = false;
+ bool convertedToArray = false;
nlohmann::json methodResponse;
nlohmann::json arguments;
};
@@ -1190,6 +1191,64 @@
sdbusplus::message::message &m,
const std::string &returnType)
{
+ nlohmann::json data;
+
+ int r = convertDBusToJSON(returnType, m, data);
+ if (r < 0)
+ {
+ transaction->outputFailed = true;
+ return;
+ }
+
+ if (data.is_null())
+ {
+ return;
+ }
+
+ if (transaction->methodResponse.is_null())
+ {
+ transaction->methodResponse = std::move(data);
+ return;
+ }
+
+ // If they're both dictionaries or arrays, merge into one.
+ // Otherwise, make the results an array with every result
+ // an entry. Could also just fail in that case, but it
+ // seems better to get the data back somehow.
+
+ if (transaction->methodResponse.is_object() && data.is_object())
+ {
+ for (const auto &obj : data.items())
+ {
+ // Note: Will overwrite the data for a duplicate key
+ transaction->methodResponse.emplace(obj.key(),
+ std::move(obj.value()));
+ }
+ return;
+ }
+
+ if (transaction->methodResponse.is_array() && data.is_array())
+ {
+ for (auto &obj : data)
+ {
+ transaction->methodResponse.push_back(std::move(obj));
+ }
+ return;
+ }
+
+ if (!transaction->convertedToArray)
+ {
+ // They are different types. May as well turn them into an array
+ nlohmann::json j = std::move(transaction->methodResponse);
+ transaction->methodResponse = nlohmann::json::array();
+ transaction->methodResponse.push_back(std::move(j));
+ transaction->methodResponse.push_back(std::move(data));
+ transaction->convertedToArray = true;
+ }
+ else
+ {
+ transaction->methodResponse.push_back(std::move(data));
+ }
}
void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,