summaryrefslogtreecommitdiffstats
path: root/server/src/rwgps.cpp
diff options
context:
space:
mode:
authorRutger Broekhoff2026-08-29 12:01:42 +0200
committerRutger Broekhoff2026-08-29 12:01:42 +0200
commit52b3cd46c76fd4be18aeb8422a08a073484b9fad (patch)
treeb4f23957c096e6bce5369bb94a4e8e23de71761a /server/src/rwgps.cpp
parent35878b76049b9c3e752480e9f298b7e2da6fdf1f (diff)
downloadroutemon-52b3cd46c76fd4be18aeb8422a08a073484b9fad.tar.gz
routemon-52b3cd46c76fd4be18aeb8422a08a073484b9fad.zip
More module implementation partition units
Diffstat (limited to 'server/src/rwgps.cpp')
-rw-r--r--server/src/rwgps.cpp150
1 files changed, 150 insertions, 0 deletions
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 @@
1module;
2
3#include <boost/beast/core.hpp>
4#include <boost/beast/http.hpp>
5#include <boost/json.hpp>
6
7module routemon:rwgps$impl;
8
9import :rwgps;
10
11namespace beast = boost::beast;
12namespace bhttp = beast::http;
13namespace json = boost::json;
14
15namespace routemon::rwgps {
16
17export struct route_summary
18{
19 std::int64_t id;
20 std::int64_t user_id;
21 std::string url;
22 std::string name;
23 std::string description;
24};
25
26struct pagination
27{
28 std::size_t record_count;
29 std::size_t page_count;
30 std::size_t page_size;
31 std::optional<std::string> next_page_url;
32};
33
34struct get_routes_meta
35{
36 pagination pagination;
37};
38
39struct get_routes_response
40{
41 std::vector<route_summary> routes;
42 get_routes_meta meta;
43};
44
45auto tag_invoke(json::value_to_tag<route_summary> const&, json::value const& jv)
46 -> route_summary
47{
48 return {
49 .id = json::value_to<std::int64_t>(jv.at("id")),
50 .user_id = json::value_to<std::int64_t>(jv.at("user_id")),
51 .url = json::value_to<std::string>(jv.at("url")),
52 .name = json::value_to<std::string>(jv.at("name")),
53 .description = json::value_to<std::string>(jv.at("description")),
54 };
55}
56
57auto tag_invoke(json::value_to_tag<pagination> const&, json::value const& jv)
58 -> pagination
59{
60 return {
61 .record_count = json::value_to<std::size_t>(jv.at("record_count")),
62 .page_count = json::value_to<std::size_t>(jv.at("page_count")),
63 .page_size = json::value_to<std::size_t>(jv.at("page_size")),
64 .next_page_url =
65 json::value_to<std::optional<std::string>>(jv.at("next_page_url")),
66 };
67}
68
69auto tag_invoke(
70 json::value_to_tag<get_routes_meta> const&, json::value const& jv)
71 -> get_routes_meta
72{
73 return {
74 .pagination = json::value_to<pagination>(jv.at("pagination")),
75 };
76}
77
78auto tag_invoke(
79 json::value_to_tag<get_routes_response> const&, json::value const& jv)
80 -> get_routes_response
81{
82 return {
83 .routes = json::value_to<std::vector<route_summary>>(jv.at("routes")),
84 .meta = json::value_to<get_routes_meta>(jv.at("meta")),
85 };
86}
87
88auto json_value_to_get_routes_response(json::value const& jv)
89 -> get_routes_response
90{
91 return json::value_to<get_routes_response>(jv);
92}
93
94constexpr std::string host = "ridewithgps.com";
95
96// TODO: handle failure appropriately
97auto client::get_routes_page(std::size_t page) -> get_routes_response
98{
99 auto req = bhttp::request<bhttp::string_body>{
100 bhttp::verb::get,
101 std::format("/api/v1/routes.json?page_size=200?page={}", page),
102 11, // HTTP 1.1
103 };
104 req.set(bhttp::field::host, host);
105 req.set("x-rwgps-api-key", api_key_);
106 req.set("x-rwgps-auth-token", auth_token_);
107
108 auto rsp = hc_.do_request(req);
109 auto p = json::stream_parser{};
110 for (auto const frag : rsp.body().cdata())
111 p.write(static_cast<char const*>(frag.data()), frag.size());
112 assert(p.done());
113 return json_value_to_get_routes_response(p.release());
114}
115
116client::client(
117 net::io_context& ioc, log::logger const& l, std::string api_key,
118 std::string auth_token)
119 : l_{l.sub("rwgps-client")}, hc_{ioc}, api_key_{std::move(api_key)},
120 auth_token_{std::move(auth_token)}
121{
122}
123
124auto client::get_all_routes() -> std::vector<route_summary>
125{
126 // TODO: make sure that there are no duplicates here.
127 // What does RWGPS sort on, by default?
128 // Consider using an associative container instead of a vector.
129 auto record_count = 0uz;
130 auto current_page = 0uz;
131 auto routes = std::vector<route_summary>{};
132
133 while (true)
134 {
135 auto rsp = get_routes_page(current_page);
136 if (rsp.meta.pagination.next_page_url)
137 l_.debug("Next page URL: {}", *rsp.meta.pagination.next_page_url);
138 routes.append_range(rsp.routes);
139 if (rsp.meta.pagination.record_count > 0)
140 record_count = rsp.meta.pagination.record_count;
141 if (rsp.routes.empty() || routes.size() >= record_count)
142 {
143 break;
144 }
145 }
146
147 return routes;
148}
149
150} // namespace routemon::rwgps