From 52b3cd46c76fd4be18aeb8422a08a073484b9fad Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Sat, 29 Aug 2026 12:01:42 +0200 Subject: More module implementation partition units --- server/src/rwgps.cpp | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 server/src/rwgps.cpp (limited to 'server/src/rwgps.cpp') diff --git a/server/src/rwgps.cpp b/server/src/rwgps.cpp new file mode 100644 index 0000000..b8ebf11 --- /dev/null +++ b/server/src/rwgps.cpp @@ -0,0 +1,150 @@ +module; + +#include +#include +#include + +module routemon:rwgps$impl; + +import :rwgps; + +namespace beast = boost::beast; +namespace bhttp = beast::http; +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); +} + +constexpr std::string host = "ridewithgps.com"; + +// TODO: handle failure appropriately +auto client::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()); +} + +client::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 client::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