| #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 |