ReUseX  0.0.5
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
api.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Povl Filip Sonne-Frederiksen
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7// The GUI API surface, expressed as plain functions over a ProjectDB.
8//
9// Everything here is deliberately HTTP-framework-free: handlers take a
10// ProjectDB (plus already-parsed parameters) and return JSON or bytes, and
11// signal failure by throwing HttpError. gui/Server.hpp is the only place that
12// knows about Crow. That split is what makes the contract unit-testable
13// (tests/unit/rux_gui/) without standing up a socket.
14//
15// The contract these functions implement is docs/gui/openapi.yaml. Any change
16// here that alters a response shape is a change to that document too.
17
18#include <reusex/pipeline/JobRunner.hpp>
19#include <reusex/pipeline/stages.hpp>
20
21#include <nlohmann/json.hpp>
22
23#include <cstdint>
24#include <filesystem>
25#include <functional>
26#include <map>
27#include <optional>
28#include <stdexcept>
29#include <string>
30#include <string_view>
31#include <utility>
32#include <vector>
33
34namespace reusex {
35class ProjectDB;
36}
37
38namespace rux::gui {
39
42inline constexpr std::string_view kApiVersion = "1.0.0";
43inline constexpr std::string_view kApiPrefix = "/api/v1";
45inline constexpr std::string_view kImplementation = "rux-gui";
46
47// --- paging ---------------------------------------------------------------
48//
49// Every collection that grows with the size of a scan is paged the same way:
50// `offset` + `limit` in, an `offset`/`count`/`total` envelope out. `limit=0`
51// means "the server maximum" — a real number per resource, never "unbounded",
52// so no request can ask the server to materialize an arbitrarily large
53// response. The maxima differ only where the per-item cost differs.
54
57inline constexpr size_t kMaxPointsPerPage = 1000000;
58inline constexpr size_t kDefaultPointsPerPage = 100000;
59
63inline constexpr int kMaxLogEntries = 1000;
64inline constexpr int kDefaultLogEntries = 100;
65
70inline constexpr int kMaxCollectionItems = 1000;
71
75inline constexpr int kMaxFrameIds = 10000;
76
80inline constexpr int kMaxImageSize = 4096;
81
85inline constexpr int kWriteLockTimeoutMs = 250;
86
88class HttpError : public std::runtime_error {
89 public:
90 HttpError(int status, std::string message)
91 : std::runtime_error(std::move(message)), status_(status) {}
92
93 int status() const noexcept { return status_; }
94
95 private:
96 int status_;
97};
98
100struct Blob {
101 std::string content_type;
102 std::vector<uint8_t> data;
103};
104
107struct Endpoint {
108 std::string method;
109 std::string path;
110 std::string summary;
111 bool binary = false;
112};
113
115const std::vector<Endpoint> &endpoint_table();
116
118class Params {
119 public:
120 void set(std::string key, std::string value);
121 std::optional<std::string> find(std::string_view key) const;
122
124 std::string str(std::string_view key, std::string fallback) const;
125
128 long long integer(std::string_view key, long long fallback) const;
129
136 std::optional<bool> boolean(std::string_view key) const;
137
138 private:
139 std::map<std::string, std::string, std::less<>> values_;
140};
141
142// --- paging ---------------------------------------------------------------
143//
144// Declared after Params, which these take by reference.
145
148 uint64_t offset = 0;
149 uint64_t limit = 0;
150};
151
159 size_t first = 0;
160 size_t last = 0;
161 size_t total = 0;
162
163 size_t count() const noexcept { return last - first; }
164};
165
174PageRequest parse_page_request(const Params &params, long long default_limit,
175 long long max_limit);
176
178PageWindow page_window(const PageRequest &page, size_t total);
179
185void add_page_envelope(nlohmann::json &object, const PageWindow &window);
186
187// --- error / meta ---------------------------------------------------------
188
189nlohmann::json error_json(int status, std::string_view message);
190
194nlohmann::json health_json(const reusex::ProjectDB *db,
195 const std::filesystem::path &project);
196
197nlohmann::json endpoints_json();
198
199// --- project --------------------------------------------------------------
200
204nlohmann::json project_summary_json(const reusex::ProjectDB &db);
205
206nlohmann::json projects_json(const reusex::ProjectDB &db, const Params &params);
207
208// --- clouds ---------------------------------------------------------------
209
210nlohmann::json clouds_json(const reusex::ProjectDB &db, const Params &params);
211nlohmann::json cloud_json(const reusex::ProjectDB &db, const std::string &name);
212
215nlohmann::json cloud_labels_json(const reusex::ProjectDB &db,
216 const std::string &name);
217
232nlohmann::json cloud_points_json(const reusex::ProjectDB &db,
233 const std::string &name, const Params &params);
234
239 std::optional<nlohmann::json> body;
240 std::optional<Blob> blob;
244 std::vector<std::pair<std::string, std::string>> headers;
245};
246
251 const std::string &name, const Params &params);
252
253// --- meshes ---------------------------------------------------------------
254
255nlohmann::json meshes_json(const reusex::ProjectDB &db, const Params &params);
256nlohmann::json mesh_json(const reusex::ProjectDB &db, const std::string &name);
257nlohmann::json mesh_textures_json(const reusex::ProjectDB &db,
258 const std::string &name);
259Blob mesh_data_blob(const reusex::ProjectDB &db, const std::string &name);
260Blob mesh_texture_blob(const reusex::ProjectDB &db, const std::string &name,
261 const std::string &texture);
262
263// --- sensor frames --------------------------------------------------------
264
271 bool valid = false;
272 double min = 0.0;
273 double max = 0.0;
274};
275
281
283nlohmann::json frames_json(const reusex::ProjectDB &db, const Params &params);
284nlohmann::json frame_json(const reusex::ProjectDB &db, int id);
285
292 const Params &params);
293
294// --- panoramas ------------------------------------------------------------
295
296nlohmann::json panoramas_json(const reusex::ProjectDB &db,
297 const Params &params);
298nlohmann::json panorama_json(const reusex::ProjectDB &db, int id);
299
306 const Params &params);
307
308// --- components / materials / instances -----------------------------------
309
310nlohmann::json components_json(const reusex::ProjectDB &db,
311 const Params &params);
312nlohmann::json component_json(const reusex::ProjectDB &db,
313 const std::string &name);
314nlohmann::json materials_json(const reusex::ProjectDB &db,
315 const Params &params);
316nlohmann::json material_json(const reusex::ProjectDB &db,
317 const std::string &guid);
318nlohmann::json instances_json(const reusex::ProjectDB &db,
319 const std::string &cloud, const Params &params);
320
321// --- pipeline -------------------------------------------------------------
322
323nlohmann::json stages_json(const reusex::ProjectDB &db);
324
331 const std::string &stage);
332nlohmann::json pipeline_log_json(const reusex::ProjectDB &db,
333 const Params &params);
334
335// --- jobs -----------------------------------------------------------------
336
340nlohmann::json job_json(const reusex::pipeline::JobRecord &record,
341 std::string_view project);
342
348nlohmann::json jobs_json(const std::vector<reusex::pipeline::JobRecord> &jobs,
349 std::string_view project);
350
352nlohmann::json
353jobs_page_json(const std::vector<reusex::pipeline::JobRecord> &jobs,
354 std::string_view project, const Params &params);
355nlohmann::json job_event_json(const reusex::pipeline::JobEvent &event,
356 std::string_view project);
357nlohmann::json hello_json(const std::vector<reusex::pipeline::JobRecord> &jobs,
358 const std::filesystem::path &project);
359
370
374JobSubmission parse_job_request(std::string_view body);
375
379void check_job_project(const JobSubmission &submission,
380 std::string_view open_project);
381
387std::optional<nlohmann::json> handle_ws_message(
388 std::string_view body,
389 const std::function<void(std::optional<std::string>)> &subscribe);
390
394 const std::optional<std::string> &subscription);
395
396} // namespace rux::gui
HttpError(int status, std::string message)
Definition api.hpp:90
int status() const noexcept
Definition api.hpp:93
Already-decoded query parameters, so handlers never touch crow::request.
Definition api.hpp:118
std::optional< std::string > find(std::string_view key) const
long long integer(std::string_view key, long long fallback) const
Integer value, or fallback when absent.
void set(std::string key, std::string value)
std::string str(std::string_view key, std::string fallback) const
String value, or fallback when absent/empty.
std::optional< bool > boolean(std::string_view key) const
Boolean value, or nullopt when absent/empty.
JobStage
A pipeline stage that can be executed as a job.
Definition stages.hpp:42
@ clouds
back-project sensor frames into "cloud" + "normals"
Definition stages.hpp:43
PageWindow page_window(const PageRequest &page, size_t total)
Clamp page onto a collection of total items.
constexpr int kWriteLockTimeoutMs
How long a mutating request waits for the project's writer lock before answering 503.
Definition api.hpp:85
constexpr std::string_view kApiPrefix
Definition api.hpp:43
nlohmann::json error_json(int status, std::string_view message)
constexpr std::string_view kApiVersion
Contract version served under kApiPrefix.
Definition api.hpp:42
nlohmann::json mesh_textures_json(const reusex::ProjectDB &db, const std::string &name)
nlohmann::json health_json(const reusex::ProjectDB *db, const std::filesystem::path &project)
nlohmann::json project_summary_json(const reusex::ProjectDB &db)
The dashboard payload.
nlohmann::json cloud_labels_json(const reusex::ProjectDB &db, const std::string &name)
Label id → name for one cloud, as {"labels": {...}}.
nlohmann::json endpoints_json()
nlohmann::json components_json(const reusex::ProjectDB &db, const Params &params)
constexpr int kDefaultLogEntries
Definition api.hpp:64
ImageResponse frame_image(const reusex::ProjectDB &db, int id, const Params &params)
One of a frame's images, encoded as PNG.
nlohmann::json frames_json(const reusex::ProjectDB &db, const Params &params)
constexpr int kMaxCollectionItems
Upper bound on limit for the ordinary object collections (clouds, meshes, panoramas,...
Definition api.hpp:70
nlohmann::json jobs_page_json(const std::vector< reusex::pipeline::JobRecord > &jobs, std::string_view project, const Params &params)
GET /jobs: one page of the job store, with the shared envelope.
const std::vector< Endpoint > & endpoint_table()
Every endpoint this server serves, in documentation order.
nlohmann::json cloud_json(const reusex::ProjectDB &db, const std::string &name)
constexpr size_t kMaxPointsPerPage
Upper bound on limit for the paged points endpoint, so a stray query cannot ask the server to materia...
Definition api.hpp:57
nlohmann::json mesh_json(const reusex::ProjectDB &db, const std::string &name)
nlohmann::json panoramas_json(const reusex::ProjectDB &db, const Params &params)
std::optional< nlohmann::json > handle_ws_message(std::string_view body, const std::function< void(std::optional< std::string >)> &subscribe)
Handle one client message on the WebSocket channel.
nlohmann::json jobs_json(const std::vector< reusex::pipeline::JobRecord > &jobs, std::string_view project)
Every supplied job, unpaged, as {"jobs": [...]}.
nlohmann::json component_json(const reusex::ProjectDB &db, const std::string &name)
nlohmann::json panorama_json(const reusex::ProjectDB &db, int id)
nlohmann::json stages_json(const reusex::ProjectDB &db)
nlohmann::json clouds_json(const reusex::ProjectDB &db, const Params &params)
Blob mesh_texture_blob(const reusex::ProjectDB &db, const std::string &name, const std::string &texture)
nlohmann::json meshes_json(const reusex::ProjectDB &db, const Params &params)
nlohmann::json hello_json(const std::vector< reusex::pipeline::JobRecord > &jobs, const std::filesystem::path &project)
nlohmann::json stage_validation_json(const reusex::ProjectDB &db, const std::string &stage)
Input-contract validation for a single stage.
nlohmann::json pipeline_log_json(const reusex::ProjectDB &db, const Params &params)
nlohmann::json material_json(const reusex::ProjectDB &db, const std::string &guid)
nlohmann::json instances_json(const reusex::ProjectDB &db, const std::string &cloud, const Params &params)
nlohmann::json job_event_json(const reusex::pipeline::JobEvent &event, std::string_view project)
nlohmann::json job_json(const reusex::pipeline::JobRecord &record, std::string_view project)
constexpr int kMaxImageSize
Upper bound on the max_size image parameter.
Definition api.hpp:80
PointsResponse cloud_points(const reusex::ProjectDB &db, const std::string &name, const Params &params)
Serve one page of point data in the requested wire format.
nlohmann::json cloud_points_json(const reusex::ProjectDB &db, const std::string &name, const Params &params)
One page of point data as JSON, ignoring format.
constexpr size_t kDefaultPointsPerPage
Definition api.hpp:58
Blob panorama_image_blob(const reusex::ProjectDB &db, int id, const Params &params)
The equirectangular image, JPEG-encoded.
constexpr int kMaxFrameIds
Upper bound on limit for the sensor-frame id list.
Definition api.hpp:75
JobSubmission parse_job_request(std::string_view body)
Parse and validate a job submission body.
nlohmann::json projects_json(const reusex::ProjectDB &db, const Params &params)
constexpr std::string_view kImplementation
Identifies which implementation of the contract is answering.
Definition api.hpp:45
void add_page_envelope(nlohmann::json &object, const PageWindow &window)
Add the shared offset/count/total envelope to a collection response.
nlohmann::json frame_json(const reusex::ProjectDB &db, int id)
nlohmann::json materials_json(const reusex::ProjectDB &db, const Params &params)
void check_job_project(const JobSubmission &submission, std::string_view open_project)
Reject a submission aimed at a different project.
PageRequest parse_page_request(const Params &params, long long default_limit, long long max_limit)
Parse and clamp offset/limit for a paged collection.
bool event_matches_subscription(const reusex::pipeline::JobEvent &event, const std::optional< std::string > &subscription)
True when event should be delivered to a connection filtered to subscription (nullopt = unfiltered,...
Blob mesh_data_blob(const reusex::ProjectDB &db, const std::string &name)
constexpr int kMaxLogEntries
Upper bound on limit for the pipeline-log endpoint.
Definition api.hpp:63
One notification about a job.
A job as reported by the runner. Snapshot; never a live view.
Definition JobRunner.hpp:74
A non-JSON response body (image, mesh blob).
Definition api.hpp:100
std::vector< uint8_t > data
Definition api.hpp:102
std::string content_type
Definition api.hpp:101
One row of the route table.
Definition api.hpp:107
bool binary
True when the response is not JSON.
Definition api.hpp:111
std::string path
Crow route pattern, e.g. "/api/v1/clouds/<string>".
Definition api.hpp:109
std::string summary
Matches the OpenAPI summary for the same path.
Definition api.hpp:110
std::string method
Definition api.hpp:108
An encoded image plus the provenance of any rendering applied to it.
Definition api.hpp:277
ValueRange range
Only populated for a normalized single-channel kind.
Definition api.hpp:279
A validated POST /jobs body.
Definition api.hpp:361
std::optional< std::string > project
Project the client believes it is addressing.
Definition api.hpp:368
std::string parameters
Serialized JSON object, "" when omitted.
Definition api.hpp:363
reusex::pipeline::JobStage stage
Definition api.hpp:362
A validated, clamped offset/limit window over a collection.
Definition api.hpp:147
The half-open [first, last) index range a page covers, plus the size of the collection it was taken f...
Definition api.hpp:158
size_t count() const noexcept
Definition api.hpp:163
One points-endpoint response.
Definition api.hpp:238
std::optional< nlohmann::json > body
Set on format=json.
Definition api.hpp:239
std::optional< Blob > blob
Set on format=binary (RUXP v1).
Definition api.hpp:240
std::vector< std::pair< std::string, std::string > > headers
X-Ruxp-* mirrors of the body header, set on the binary path only.
Definition api.hpp:244
Range of the valid stored values a normalize=true rendering mapped.
Definition api.hpp:270