10#include <llhttp/llhttp.h>
11#include <nlohmann/json.hpp>
15#include <unordered_set>
30 nlohmann::json& schema, [[maybe_unused]]
const Cose* cose)
32 schema[
"type"] =
"string";
33 schema[
"format"] =
"binary";
46 static inline nlohmann::json& get_object(
47 nlohmann::json& j,
const std::string_view& k)
49 const auto ib = j.emplace(k, nlohmann::json::object());
50 return ib.first.value();
53 static inline nlohmann::json& get_array(
54 nlohmann::json& j,
const std::string_view& k)
56 const auto ib = j.emplace(k, nlohmann::json::array());
57 return ib.first.value();
61 static inline std::string sanitise_components_key(
const std::string_view& s)
68 std::regex re(
"[^a-zA-Z0-9\\.\\-_]");
69 std::regex_replace(std::back_inserter(result), s.begin(), s.end(), re,
"_");
73 static inline nlohmann::json create_document(
74 const std::string_view& title,
75 const std::string_view& description,
76 const std::string_view& document_version)
78 return nlohmann::json{
82 {
"description", description},
83 {
"version", document_version}}},
84 {
"servers", nlohmann::json::array()},
85 {
"paths", nlohmann::json::object()}};
88 static inline nlohmann::json& server(
89 nlohmann::json& document,
const std::string_view& url)
91 auto& servers = access::get_object(document,
"servers");
92 servers.push_back({{
"url", url}});
93 return servers.back();
96 static inline nlohmann::json& path(
97 nlohmann::json& document,
const std::string_view& path)
101 if (!p.starts_with(
'/'))
103 s = fmt::format(
"/{}", p);
107 auto& paths = access::get_object(document,
"paths");
108 return access::get_object(paths, p);
111 static inline nlohmann::json& path_operation(
112 nlohmann::json& path, llhttp_method verb,
bool default_responses =
true)
115 std::string s = llhttp_method_name(verb);
116 ccf::nonstd::to_lower(s);
117 auto& po = access::get_object(path, s);
119 if (default_responses)
123 access::get_object(po,
"responses");
129 static inline nlohmann::json& parameters(nlohmann::json& path_operation)
131 return access::get_array(path_operation,
"parameters");
134 static inline nlohmann::json& responses(nlohmann::json& path_operation)
136 return access::get_object(path_operation,
"responses");
139 static inline nlohmann::json& response(
140 nlohmann::json& path_operation,
142 const std::string_view& description =
"Default response description")
144 auto& all_responses = responses(path_operation);
148 const auto s = std::to_string(status);
149 auto& response = access::get_object(all_responses, s);
150 response[
"description"] = description;
154 static inline nlohmann::json& error_response_default(
155 nlohmann::json& path_operation)
157 auto& all_responses = responses(path_operation);
158 auto& response = access::get_object(all_responses,
"default");
159 response[
"$ref"] =
"#/components/responses/default";
163 static inline nlohmann::json& request_body(nlohmann::json& path_operation)
165 auto& request_body = access::get_object(path_operation,
"requestBody");
166 access::get_object(request_body,
"content");
170 static inline nlohmann::json& media_type(
171 nlohmann::json& j,
const std::string_view& mt)
173 auto& content = access::get_object(j,
"content");
174 return access::get_object(content, mt);
177 static inline nlohmann::json& schema(nlohmann::json& media_type_object)
179 return access::get_object(media_type_object,
"schema");
182 static inline nlohmann::json& extension(
183 nlohmann::json&
object,
const std::string_view& extension_name)
185 if (!extension_name.starts_with(
"x-"))
187 throw std::logic_error(fmt::format(
188 "Adding extension with name '{}'. Extension fields must begin with "
193 return access::get_object(
object, extension_name);
200 static inline nlohmann::json components_ref_object(
201 const std::string_view& element_name)
203 auto schema_ref_object = nlohmann::json::object();
204 schema_ref_object[
"$ref"] =
205 fmt::format(
"#/components/schemas/{}", element_name);
206 return schema_ref_object;
210 static inline nlohmann::json add_schema_to_components(
211 nlohmann::json& document,
212 const std::string_view& element_name,
213 const nlohmann::json& schema_)
215 const auto name = sanitise_components_key(element_name);
217 auto& components = access::get_object(document,
"components");
218 auto& schemas = access::get_object(components,
"schemas");
220 const auto schema_it = schemas.find(name);
221 if (schema_it != schemas.end())
225 const auto& existing_schema = schema_it.value();
226 if (schema_ != existing_schema)
228 throw std::logic_error(fmt::format(
229 "Adding schema with name '{}'. Does not match previous schema "
230 "registered with this name: {} vs {}",
233 existing_schema.dump()));
238 schemas.emplace(name, schema_);
241 return components_ref_object(name);
244 static inline void add_security_scheme_to_components(
245 nlohmann::json& document,
246 const std::string_view& scheme_name,
247 const nlohmann::json& security_scheme)
249 const auto name = sanitise_components_key(scheme_name);
251 auto& components = access::get_object(document,
"components");
252 auto& schemes = access::get_object(components,
"securitySchemes");
254 const auto schema_it = schemes.find(name);
255 if (schema_it != schemes.end())
259 const auto& existing_scheme = schema_it.value();
260 if (security_scheme != existing_scheme)
262 throw std::logic_error(fmt::format(
263 "Adding security scheme with name '{}'. Does not match previous "
264 "scheme registered with this name: {} vs {}",
266 security_scheme.dump(),
267 existing_scheme.dump()));
272 schemes.emplace(name, security_scheme);
283 template <
typename Doc,
typename T>
285 [[maybe_unused]] Doc& document, nlohmann::json& j,
const T* t)
294 template <
typename T>
297 nlohmann::json schema;
300 return add_schema_component<typename T::value_type>();
307 if constexpr (std::is_same<T, std::vector<uint8_t>>::value)
310 schema[
"type"] =
"string";
311 schema[
"format"] =
"base64";
315 schema[
"type"] =
"array";
316 schema[
"items"] = add_schema_component<typename T::value_type>();
319 return add_schema_to_components(
320 document, ccf::ds::json::schema_name<T>(), schema);
326 if constexpr (nlohmann::detail::
327 is_compatible_object_type<nlohmann::json, T>::value)
329 schema[
"type"] =
"object";
330 schema[
"additionalProperties"] =
331 add_schema_component<typename T::mapped_type>();
335 schema[
"type"] =
"array";
336 auto items = nlohmann::json::object();
338 items[
"type"] =
"array";
340 auto sub_items = nlohmann::json::array();
341 sub_items.push_back(add_schema_component<typename T::key_type>());
343 add_schema_component<typename T::mapped_type>());
345 items[
"items"][
"oneOf"] = sub_items;
346 items[
"minItems"] = 2;
347 items[
"maxItems"] = 2;
349 schema[
"items"] = items;
351 return add_schema_to_components(
352 document, ccf::ds::json::schema_name<T>(), schema);
356 schema[
"type"] =
"array";
357 auto items = nlohmann::json::array();
358 items.push_back(add_schema_component<typename T::first_type>());
359 items.push_back(add_schema_component<typename T::second_type>());
360 schema[
"items"] = items;
361 return add_schema_to_components(
362 document, ccf::ds::json::schema_name<T>(), schema);
365 std::is_same<T, std::string>::value || std::is_arithmetic_v<T> ||
366 std::is_same<T, nlohmann::json>::value ||
367 std::is_same<T, ccf::ds::json::JsonSchema>::value)
369 ccf::ds::json::fill_schema<T>(schema);
370 return add_schema_to_components(
371 document, ccf::ds::json::schema_name<T>(), schema);
376 sanitise_components_key(ccf::ds::json::schema_name<T>());
378 auto& components = access::get_object(
document,
"components");
379 auto& schemas = access::get_object(components,
"schemas");
381 const auto ib = schemas.emplace(name, nlohmann::json::object());
384 auto& j = ib.first.value();
386#pragma clang diagnostic push
387#if defined(__clang__) && __clang_major__ >= 11
388# pragma clang diagnostic ignored "-Wuninitialized-const-reference"
392 if constexpr (std::is_enum<T>::value)
394 fill_enum_schema(j, t);
400#pragma clang diagnostic pop
403 return components_ref_object(name);
408 template <
typename T>
409 static inline char const* auto_content_type()
411 if constexpr (std::is_same_v<T, std::string>)
413 return http::headervalues::contenttype::TEXT;
415 else if constexpr (std::is_same_v<T, Cose>)
417 return http::headervalues::contenttype::COSE;
421 return http::headervalues::contenttype::JSON;
425 static inline void add_request_body_schema(
426 nlohmann::json& document,
427 const std::string_view& uri,
429 const std::string_view& content_type,
431 const nlohmann::json& schema_)
433 auto& rb = request_body(path_operation(path(document, uri), verb));
434 rb[
"description"] =
"Auto-generated request body schema";
436 schema(media_type(rb, content_type)) =
437 add_schema_to_components(document,
schema_name, schema_);
440 template <
typename T>
441 static inline void add_request_body_schema(
442 nlohmann::json& document,
const std::string_view& uri, llhttp_method verb)
444 auto& rb = request_body(path_operation(path(document, uri), verb));
445 rb[
"description"] =
"Auto-generated request body schema";
447 SchemaHelper sh{document};
448 const auto schema_comp = sh.add_schema_component<T>();
449 if (schema_comp !=
nullptr)
451 schema(media_type(rb, auto_content_type<T>())) =
452 sh.add_schema_component<T>();
456 static inline void add_path_parameter_schema(
457 nlohmann::json& document,
458 const std::string_view& uri,
459 const nlohmann::json& param)
461 auto& params = parameters(path(document, uri));
462 for (
auto& p : params)
464 if (p[
"name"] == param[
"name"])
469 params.push_back(param);
472 static inline void add_request_parameter_schema(
473 nlohmann::json& document,
474 const std::string_view& uri,
476 const nlohmann::json& param)
478 auto& params = parameters(path_operation(path(document, uri), verb));
479 params.push_back(param);
482 static inline void add_response_schema(
483 nlohmann::json& document,
484 const std::string_view& uri,
487 const std::string_view& content_type,
489 const nlohmann::json& schema_)
491 auto& r = response(path_operation(path(document, uri), verb), status);
493 schema(media_type(r, content_type)) =
494 add_schema_to_components(document,
schema_name, schema_);
497 template <
typename T>
498 static inline void add_response_schema(
499 nlohmann::json& document,
500 const std::string_view& uri,
504 auto& r = response(path_operation(path(document, uri), verb), status);
506 SchemaHelper sh{document};
507 const auto schema_comp = sh.add_schema_component<T>();
508 if (schema_comp !=
nullptr)
510 schema(media_type(r, auto_content_type<T>())) =
511 sh.add_schema_component<T>();
void fill_json_schema(nlohmann::json &schema, const Cose *cose)
Definition openapi.h:29
std::string schema_name(const Cose *cose)
Definition openapi.h:36
void add_schema_components(Doc &document, nlohmann::json &j, const T *t)
Definition openapi.h:284
llhttp_status http_status
Definition http_status.h:9
nlohmann::json add_schema_component()
Definition openapi.h:295
nlohmann::json & document
Definition openapi.h:292