Separate out api url parser

This commit is contained in:
2025-09-04 16:36:10 -04:00
parent 55069c0c79
commit 2278694f4f
7 changed files with 417 additions and 219 deletions

115
src/api_url_parser.cpp Normal file
View File

@@ -0,0 +1,115 @@
#include "api_url_parser.hpp"
#include <string_view>
namespace {
// A simplified helper to split a string_view, similar to std::string::find.
// Returns a pair of views, the part before the delimiter and the part after.
std::pair<std::string_view, std::string_view> split_view(std::string_view view,
char delimiter) {
size_t pos = view.find(delimiter);
if (pos == std::string_view::npos) {
return {view, ""};
}
return {view.substr(0, pos), view.substr(pos + 1)};
}
// Maps a string parameter key to its corresponding enum value.
// Unrecognized keys are ignored as per the design.
[[nodiscard]] std::optional<ApiParameterKey>
to_api_parameter_key(std::string_view key) {
if (key == "request_id")
return ApiParameterKey::RequestId;
if (key == "min_version")
return ApiParameterKey::MinVersion;
if (key == "wait")
return ApiParameterKey::Wait;
if (key == "timeout")
return ApiParameterKey::Timeout;
if (key == "verbose")
return ApiParameterKey::Verbose;
return std::nullopt;
}
// Parses the query string part of a URL.
void parse_query_string(std::string_view query_string, RouteMatch &match) {
while (!query_string.empty()) {
auto [key_value_pair, rest] = split_view(query_string, '&');
auto [key, value] = split_view(key_value_pair, '=');
if (auto key_enum = to_api_parameter_key(key)) {
match.params[static_cast<size_t>(*key_enum)] = value;
}
query_string = rest;
}
}
} // namespace
RouteMatch ApiUrlParser::parse(std::string_view method, std::string_view url) {
RouteMatch result;
auto [path, query] = split_view(url, '?');
parse_query_string(query, result);
if (method == "GET") {
if (path == "/v1/version") {
result.route = HttpRoute::GetVersion;
return result;
}
if (path == "/v1/subscribe") {
result.route = HttpRoute::GetSubscribe;
return result;
}
if (path == "/v1/status") {
result.route = HttpRoute::GetStatus;
return result;
}
if (path.starts_with("/v1/retention")) {
result.route = HttpRoute::GetRetention;
// Note: This matches both /v1/retention and /v1/retention/{id}
// The handler will need to check for the presence of the PolicyId param.
if (path.length() > 13) { // length of "/v1/retention"
std::string_view policy_id =
path.substr(14); // length of "/v1/retention/"
if (!policy_id.empty()) {
result.params[static_cast<size_t>(ApiParameterKey::PolicyId)] =
policy_id;
}
}
return result;
}
if (path == "/metrics") {
result.route = HttpRoute::GetMetrics;
return result;
}
if (path == "/ok") {
result.route = HttpRoute::GetOk;
return result;
}
} else if (method == "POST") {
if (path == "/v1/commit") {
result.route = HttpRoute::PostCommit;
return result;
}
} else if (method == "PUT") {
if (path.starts_with("/v1/retention/")) {
result.route = HttpRoute::PutRetention;
std::string_view policy_id = path.substr(14);
result.params[static_cast<size_t>(ApiParameterKey::PolicyId)] = policy_id;
return result;
}
} else if (method == "DELETE") {
if (path.starts_with("/v1/retention/")) {
result.route = HttpRoute::DeleteRetention;
std::string_view policy_id = path.substr(14);
result.params[static_cast<size_t>(ApiParameterKey::PolicyId)] = policy_id;
return result;
}
}
result.route = HttpRoute::NotFound;
return result;
}