From 973aec43ea54bbf95b64fbcb636403401d1ca60e Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Fri, 28 Aug 2026 18:03:05 +0200 Subject: Import from e4b104792206ee7ea64bf39c6b7d2c0c230f9d14 --- server/src/rwgps.cppm | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 server/src/rwgps.cppm (limited to 'server/src/rwgps.cppm') diff --git a/server/src/rwgps.cppm b/server/src/rwgps.cppm new file mode 100644 index 0000000..7ad6605 --- /dev/null +++ b/server/src/rwgps.cppm @@ -0,0 +1,137 @@ +module; + +#include +#include +#include + +export module routemon:rwgps; + +import std; +import :http.client; +import :log; + +namespace beast = boost::beast; +namespace bhttp = beast::http; +namespace net = boost::asio; +namespace json = boost::json; + +namespace routemon::rwgps { + + export struct route_summary { + std::int64_t id; + std::int64_t user_id; + std::string url; + std::string name; + std::string description; + }; + + struct pagination { + std::size_t record_count; + std::size_t page_count; + std::size_t page_size; + std::optional next_page_url; + }; + + struct get_routes_meta { + pagination pagination; + }; + + struct get_routes_response { + std::vector routes; + get_routes_meta meta; + }; + + auto tag_invoke(json::value_to_tag const&, json::value const& jv) -> route_summary { + return { + .id = json::value_to(jv.at("id")), + .user_id = json::value_to(jv.at("user_id")), + .url = json::value_to(jv.at("url")), + .name = json::value_to(jv.at("name")), + .description = json::value_to(jv.at("description")), + }; + } + + auto tag_invoke(json::value_to_tag const&, json::value const& jv) -> pagination { + return { + .record_count = json::value_to(jv.at("record_count")), + .page_count = json::value_to(jv.at("page_count")), + .page_size = json::value_to(jv.at("page_size")), + .next_page_url = json::value_to>(jv.at("next_page_url")), + }; + } + + auto tag_invoke(json::value_to_tag const&, json::value const& jv) -> get_routes_meta { + return { + .pagination = json::value_to(jv.at("pagination")), + }; + } + + auto tag_invoke(json::value_to_tag const&, json::value const& jv) -> get_routes_response { + return { + .routes = json::value_to>(jv.at("routes")), + .meta = json::value_to(jv.at("meta")), + }; + } + + auto json_value_to_get_routes_response(json::value const& jv) -> get_routes_response { + return json::value_to(jv); + } + + export class client { + log::logger l_; + http::client hc_; + std::string api_key_; + std::string auth_token_; + + static constexpr std::string host = "ridewithgps.com"; + + // TODO: handle failure appropriately + auto get_routes_page(std::size_t page) -> get_routes_response { + auto req = bhttp::request{ + bhttp::verb::get, + std::format("/api/v1/routes.json?page_size=200?page={}", page), + 11, // HTTP 1.1 + }; + req.set(bhttp::field::host, host); + req.set("x-rwgps-api-key", api_key_); + req.set("x-rwgps-auth-token", auth_token_); + + auto rsp = hc_.do_request(req); + auto p = json::stream_parser{}; + for (auto const frag : rsp.body().cdata()) { + p.write(static_cast(frag.data()), frag.size()); + } + assert(p.done()); + return json_value_to_get_routes_response(p.release()); + } + + public: + explicit client(net::io_context& ioc, log::logger const& l, std::string api_key, std::string auth_token) + : l_{l.sub("rwgps-client")}, hc_{ioc}, api_key_{std::move(api_key)}, auth_token_{std::move(auth_token)} + {} + + auto get_all_routes() -> std::vector { + // TODO: make sure that there are no duplicates here. + // What does RWGPS sort on, by default? + // Consider using an associative container instead of a vector. + auto record_count = 0uz; + auto current_page = 0uz; + auto routes = std::vector{}; + + while (true) { + auto rsp = get_routes_page(current_page); + if (rsp.meta.pagination.next_page_url) + l_.debug("Next page URL: {}", *rsp.meta.pagination.next_page_url); + routes.append_range(rsp.routes); + if (rsp.meta.pagination.record_count > 0) + record_count = rsp.meta.pagination.record_count; + if (rsp.routes.empty() || routes.size() >= record_count) { + break; + } + } + + return routes; + } + }; + +} // namespace routemon::rwgps -- cgit v1.3