27 static inline nlohmann::json& get_object(
28 nlohmann::json& j,
const std::string_view& k)
30 const auto ib = j.emplace(k, nlohmann::json::object());
31 return ib.first.value();
34 static inline nlohmann::json& get_array(
35 nlohmann::json& j,
const std::string_view& k)
37 const auto ib = j.emplace(k, nlohmann::json::array());
38 return ib.first.value();
42 static inline std::string sanitise_components_key(
const std::string_view& s)
49 std::regex re(
"[^a-zA-Z0-9\\.\\-_]");
50 std::regex_replace(std::back_inserter(result), s.begin(), s.end(), re,
"_");
54 static inline nlohmann::json create_document(
55 const std::string_view& title,
56 const std::string_view& description,
57 const std::string_view& document_version)
59 return nlohmann::json{
63 {
"description", description},
64 {
"version", document_version}}},
65 {
"servers", nlohmann::json::array()},
66 {
"paths", nlohmann::json::object()}};
69 static inline nlohmann::json& server(
70 nlohmann::json& document,
const std::string_view& url)
72 auto& servers = access::get_object(document,
"servers");
73 servers.push_back({{
"url", url}});
74 return servers.back();
77 static inline nlohmann::json& path(
78 nlohmann::json& document,
const std::string_view& path)
82 if (!p.starts_with(
'/'))
84 s = fmt::format(
"/{}", p);
88 auto& paths = access::get_object(document,
"paths");
89 return access::get_object(paths, p);
92 static inline nlohmann::json& path_operation(
93 nlohmann::json& path, llhttp_method verb,
bool default_responses =
true)
96 std::string s = llhttp_method_name(verb);
97 ccf::nonstd::to_lower(s);
98 auto& po = access::get_object(path, s);
100 if (default_responses)
104 access::get_object(po,
"responses");
110 static inline nlohmann::json& parameters(nlohmann::json& path_operation)
112 return access::get_array(path_operation,
"parameters");
115 static inline nlohmann::json& responses(nlohmann::json& path_operation)
117 return access::get_object(path_operation,
"responses");
120 static inline nlohmann::json& response(
121 nlohmann::json& path_operation,
123 const std::string_view& description =
"Default response description")
125 auto& all_responses = responses(path_operation);
129 const auto s = std::to_string(status);
130 auto& response = access::get_object(all_responses, s);
131 response[
"description"] = description;
135 static inline nlohmann::json& error_response_default(
136 nlohmann::json& path_operation)
138 auto& all_responses = responses(path_operation);
139 auto& response = access::get_object(all_responses,
"default");
140 response[
"$ref"] =
"#/components/responses/default";
144 static inline nlohmann::json& request_body(nlohmann::json& path_operation)
146 auto& request_body = access::get_object(path_operation,
"requestBody");
147 access::get_object(request_body,
"content");
151 static inline nlohmann::json& media_type(
152 nlohmann::json& j,
const std::string_view& mt)
154 auto& content = access::get_object(j,
"content");
155 return access::get_object(content, mt);
158 static inline nlohmann::json& schema(nlohmann::json& media_type_object)
160 return access::get_object(media_type_object,
"schema");
163 static inline nlohmann::json& extension(
164 nlohmann::json&
object,
const std::string_view& extension_name)
166 if (!extension_name.starts_with(
"x-"))
168 throw std::logic_error(fmt::format(
169 "Adding extension with name '{}'. Extension fields must begin with "
174 return access::get_object(
object, extension_name);
181 static inline nlohmann::json components_ref_object(
182 const std::string_view& element_name)
184 auto schema_ref_object = nlohmann::json::object();
185 schema_ref_object[
"$ref"] =
186 fmt::format(
"#/components/schemas/{}", element_name);
187 return schema_ref_object;
191 static inline nlohmann::json add_schema_to_components(
192 nlohmann::json& document,
193 const std::string_view& element_name,
194 const nlohmann::json& schema_)
196 const auto name = sanitise_components_key(element_name);
198 auto& components = access::get_object(document,
"components");
199 auto& schemas = access::get_object(components,
"schemas");
201 const auto schema_it = schemas.find(name);
202 if (schema_it != schemas.end())
206 const auto& existing_schema = schema_it.value();
207 if (schema_ != existing_schema)
209 throw std::logic_error(fmt::format(
210 "Adding schema with name '{}'. Does not match previous schema "
211 "registered with this name: {} vs {}",
214 existing_schema.dump()));
219 schemas.emplace(name, schema_);
222 return components_ref_object(name);
225 static inline void add_security_scheme_to_components(
226 nlohmann::json& document,
227 const std::string_view& scheme_name,
228 const nlohmann::json& security_scheme)
230 const auto name = sanitise_components_key(scheme_name);
232 auto& components = access::get_object(document,
"components");
233 auto& schemes = access::get_object(components,
"securitySchemes");
235 const auto schema_it = schemes.find(name);
236 if (schema_it != schemes.end())
240 const auto& existing_scheme = schema_it.value();
241 if (security_scheme != existing_scheme)
243 throw std::logic_error(fmt::format(
244 "Adding security scheme with name '{}'. Does not match previous "
245 "scheme registered with this name: {} vs {}",
247 security_scheme.dump(),
248 existing_scheme.dump()));
253 schemes.emplace(name, security_scheme);
264 template <
typename Doc,
typename T>
266 [[maybe_unused]] Doc& document, nlohmann::json& j,
const T* t)
275 template <
typename T>
278 nlohmann::json schema;
281 return add_schema_component<typename T::value_type>();
288 if constexpr (std::is_same<T, std::vector<uint8_t>>::value)
291 schema[
"type"] =
"string";
292 schema[
"format"] =
"base64";
296 schema[
"type"] =
"array";
297 schema[
"items"] = add_schema_component<typename T::value_type>();
300 return add_schema_to_components(
301 document, ccf::ds::json::schema_name<T>(), schema);
307 if constexpr (nlohmann::detail::
308 is_compatible_object_type<nlohmann::json, T>::value)
310 schema[
"type"] =
"object";
311 schema[
"additionalProperties"] =
312 add_schema_component<typename T::mapped_type>();
316 schema[
"type"] =
"array";
317 auto items = nlohmann::json::object();
319 items[
"type"] =
"array";
321 auto sub_items = nlohmann::json::array();
322 sub_items.push_back(add_schema_component<typename T::key_type>());
324 add_schema_component<typename T::mapped_type>());
326 items[
"items"][
"oneOf"] = sub_items;
327 items[
"minItems"] = 2;
328 items[
"maxItems"] = 2;
330 schema[
"items"] = items;
332 return add_schema_to_components(
333 document, ccf::ds::json::schema_name<T>(), schema);
337 schema[
"type"] =
"array";
338 auto items = nlohmann::json::array();
339 items.push_back(add_schema_component<typename T::first_type>());
340 items.push_back(add_schema_component<typename T::second_type>());
341 schema[
"items"] = items;
342 return add_schema_to_components(
343 document, ccf::ds::json::schema_name<T>(), schema);
346 std::is_same<T, std::string>::value || std::is_arithmetic_v<T> ||
347 std::is_same<T, nlohmann::json>::value ||
348 std::is_same<T, ccf::ds::json::JsonSchema>::value)
350 ccf::ds::json::fill_schema<T>(schema);
351 return add_schema_to_components(
352 document, ccf::ds::json::schema_name<T>(), schema);
357 sanitise_components_key(ccf::ds::json::schema_name<T>());
359 auto& components = access::get_object(
document,
"components");
360 auto& schemas = access::get_object(components,
"schemas");
362 const auto ib = schemas.emplace(name, nlohmann::json::object());
365 auto& j = ib.first.value();
367#pragma clang diagnostic push
368#if defined(__clang__) && __clang_major__ >= 11
369# pragma clang diagnostic ignored "-Wuninitialized-const-reference"
373 if constexpr (std::is_enum<T>::value)
375 fill_enum_schema(j, t);
381#pragma clang diagnostic pop
384 return components_ref_object(name);
389 template <
typename T>
390 static inline char const* auto_content_type()
392 if constexpr (std::is_same_v<T, std::string>)
394 return http::headervalues::contenttype::TEXT;
398 return http::headervalues::contenttype::JSON;
402 static inline void add_request_body_schema(
403 nlohmann::json& document,
404 const std::string_view& uri,
406 const std::string_view& content_type,
408 const nlohmann::json& schema_)
410 auto&
rb = request_body(path_operation(path(document, uri), verb));
411 rb[
"description"] =
"Auto-generated request body schema";
413 schema(media_type(
rb, content_type)) =
414 add_schema_to_components(document,
schema_name, schema_);
417 template <
typename T>
418 static inline void add_request_body_schema(
419 nlohmann::json& document,
const std::string_view& uri, llhttp_method verb)
421 auto&
rb = request_body(path_operation(path(document, uri), verb));
422 rb[
"description"] =
"Auto-generated request body schema";
424 SchemaHelper sh{document};
425 const auto schema_comp = sh.add_schema_component<T>();
426 if (schema_comp !=
nullptr)
428 schema(media_type(
rb, auto_content_type<T>())) =
429 sh.add_schema_component<T>();
433 static inline void add_path_parameter_schema(
434 nlohmann::json& document,
435 const std::string_view& uri,
436 const nlohmann::json& param)
438 auto& params = parameters(path(document, uri));
439 for (
auto& p : params)
441 if (p[
"name"] == param[
"name"])
446 params.push_back(param);
449 static inline void add_request_parameter_schema(
450 nlohmann::json& document,
451 const std::string_view& uri,
453 const nlohmann::json& param)
455 auto& params = parameters(path_operation(path(document, uri), verb));
456 params.push_back(param);
459 static inline void add_response_schema(
460 nlohmann::json& document,
461 const std::string_view& uri,
464 const std::string_view& content_type,
466 const nlohmann::json& schema_)
468 auto& r = response(path_operation(path(document, uri), verb), status);
470 schema(media_type(r, content_type)) =
471 add_schema_to_components(document,
schema_name, schema_);
474 template <
typename T>
475 static inline void add_response_schema(
476 nlohmann::json& document,
477 const std::string_view& uri,
481 auto& r = response(path_operation(path(document, uri), verb), status);
483 SchemaHelper sh{document};
484 const auto schema_comp = sh.add_schema_component<T>();
485 if (schema_comp !=
nullptr)
487 schema(media_type(r, auto_content_type<T>())) =
488 sh.add_schema_component<T>();