blob: 21827c3ca11b6c1af79f2854064a7494cf1b46a2 [file] [log] [blame]
#include "tlbmc/redfish/query_parameters.h"
#include <cstddef>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "boost/url/params_view.hpp"
#include "nlohmann/json.hpp"
namespace milotic_tlbmc {
std::string ExpandTypeToUrlParam(ExpandType expand_type) {
switch (expand_type) {
case ExpandType::kNoLinks:
return ".";
case ExpandType::kLinksOnly:
return "~";
case ExpandType::kAll:
return "*";
default:
return "";
}
}
absl::StatusOr<QueryParameters> QueryParameters::Create(
boost::urls::params_view request_query_parameters) {
QueryParameters query_parameters;
if (request_query_parameters.empty()) {
return query_parameters;
}
// Currently we only support one parameter
if (request_query_parameters.size() > 1) {
return absl::InvalidArgumentError(
"Multiple query parameters are not supported.");
}
for (const boost::urls::params_view::value_type it :
request_query_parameters) {
if (it.key != "$expand") {
return absl::InvalidArgumentError("Unsupported query parameter.");
}
// We have to parse the expand type and levels if applicable.
// Examples: $expand=. or $expand=.($levels=2)
// We support the following
// expand=. (Expand with no links)
// expand=~ (Expand only links)
// expand=* (Expand all)
absl::string_view param_value = it.value;
if (absl::StartsWith(param_value, ".")) {
query_parameters.SetExpandType(ExpandType::kNoLinks);
param_value = absl::StripPrefix(param_value, ".");
} else if (absl::StartsWith(param_value, "~")) {
query_parameters.SetExpandType(ExpandType::kLinksOnly);
param_value = absl::StripPrefix(param_value, "~");
} else if (absl::StartsWith(param_value, "*")) {
query_parameters.SetExpandType(ExpandType::kAll);
param_value = absl::StripPrefix(param_value, "*");
}
// Expand level is defaulted to 1.
query_parameters.SetExpandLevel(1);
if (param_value.empty()) {
return query_parameters;
}
// Get levels if exists
if (!absl::StartsWith(param_value, "($levels=") ||
!absl::EndsWith(param_value, ")")) {
return absl::InvalidArgumentError(
absl::StrFormat("Invalid Expand Syntax. Got: %s", param_value));
}
param_value = absl::StripPrefix(param_value, "($levels=");
param_value = absl::StripSuffix(param_value, ")");
int expand_level = 0;
if (!absl::SimpleAtoi(param_value, &expand_level)) {
return absl::InvalidArgumentError(absl::StrFormat(
"Expand Level was not a number. Got: %s", param_value));
}
query_parameters.SetExpandLevel(expand_level);
}
return query_parameters;
}
namespace {
void GetUnexpandedUrlsHelper(const nlohmann::json& resp_json,
const nlohmann::json::json_pointer& cur_pointer,
ExpandType expand_type, bool in_links,
std::vector<ExpandNode>& unexpanded_urls) {
if (resp_json.is_primitive()) {
return;
}
if (resp_json.is_array()) {
for (size_t i = 0; i < resp_json.size(); ++i) {
GetUnexpandedUrlsHelper(resp_json[i], cur_pointer / i, expand_type,
in_links, unexpanded_urls);
}
return;
}
if (resp_json.is_object()) {
// Unexpanded URLs are objects with only an @odata.id
if (resp_json.size() == 1 && resp_json.contains("@odata.id")) {
unexpanded_urls.push_back(ExpandNode{.json_pointer = cur_pointer,
.url = resp_json["@odata.id"]});
return;
}
// Otherwise recurse on object values
for (const auto& [key, value] : resp_json.items()) {
bool local_in_links = in_links;
if (!local_in_links) {
// Check if this is a links node
local_in_links = key == "Links";
}
// Only traverse the parts of the tree the user asked for
// Per section 7.3 of the redfish specification
if (local_in_links && expand_type == ExpandType::kNoLinks) {
continue;
}
if (!local_in_links && expand_type == ExpandType::kLinksOnly) {
continue;
}
GetUnexpandedUrlsHelper(value, cur_pointer / key, expand_type,
local_in_links, unexpanded_urls);
}
}
}
} // namespace
std::vector<ExpandNode> GetUnexpandedUrls(const nlohmann::json& resp,
ExpandType expand_type) {
std::vector<ExpandNode> unexpanded_urls;
GetUnexpandedUrlsHelper(resp, nlohmann::json::json_pointer(""), expand_type,
false, unexpanded_urls);
return unexpanded_urls;
}
} // namespace milotic_tlbmc