Line data Source code
1 : #ifndef THINGER_HTTP_OPENAPI_HPP
2 : #define THINGER_HTTP_OPENAPI_HPP
3 :
4 : #include "routing/route_handler.hpp"
5 : #include "body_decoders.hpp"
6 : #include "../../util/types.hpp"
7 : #include <nlohmann/json.hpp>
8 : #include <functional>
9 : #include <string>
10 : #include <vector>
11 :
12 : namespace thinger::http {
13 :
14 : // Builds an OpenAPI 3.1 document from the routes of a router: paths and parameters from
15 : // the route patterns, and the rest from the route documentation (summary, tags, params,
16 : // responses, examples), its body schema and the body types it accepts (one entry of the
17 : // request body content per media type), and the limits set on the route as extensions
18 : // (x-max-body-size in bytes, x-timeout-ms). Hidden routes are left out.
19 : //
20 : // The library does not interpret route metadata: use the hooks to turn it into whatever
21 : // the application needs (x- extensions, security requirements...).
22 : class openapi_generator {
23 : public:
24 : // Called for each operation with its route, HTTP method (lowercase) and the operation
25 : // object already filled, so it can be completed or changed
26 : using operation_hook = sync_callback<void(const route&, const std::string& method, nlohmann::json& operation)>;
27 :
28 : // Called with the whole document once built (e.g. to add securitySchemes)
29 : using document_hook = sync_callback<void(nlohmann::json& document)>;
30 :
31 : // `bodies`: the body decoders and the types accepted by default (those of the
32 : // application, see http_application::body_decoders); the defaults if null
33 11202 : explicit openapi_generator(const route_handler& router, const body_settings* bodies = nullptr)
34 56010 : : router_(router), bodies_(bodies) {}
35 :
36 16 : openapi_generator& title(const std::string& value) { title_ = value; return *this; }
37 12 : openapi_generator& version(const std::string& value) { version_ = value; return *this; }
38 12 : openapi_generator& description(const std::string& value) { description_ = value; return *this; }
39 : openapi_generator& server(const std::string& url, const std::string& description = "");
40 :
41 3 : openapi_generator& on_operation(operation_hook hook) { operation_hooks_.push_back(std::move(hook)); return *this; }
42 3 : openapi_generator& on_document(document_hook hook) { document_hooks_.push_back(std::move(hook)); return *this; }
43 :
44 : nlohmann::json generate() const;
45 :
46 : // Convert a route pattern to an OpenAPI path: /users/:id([0-9]+) -> /users/{id}
47 : static std::string to_openapi_path(const std::string& pattern);
48 :
49 : private:
50 : nlohmann::json build_operation(const route& r) const;
51 :
52 : const route_handler& router_;
53 : const body_settings* bodies_;
54 : std::string title_ = "API";
55 : std::string version_ = "1.0.0";
56 : std::string description_;
57 : nlohmann::json servers_ = nlohmann::json::array();
58 : std::vector<operation_hook> operation_hooks_;
59 : std::vector<document_hook> document_hooks_;
60 : };
61 :
62 : } // namespace thinger::http
63 :
64 : #endif // THINGER_HTTP_OPENAPI_HPP
|