-
Notifications
You must be signed in to change notification settings - Fork 4
handle in path params #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| #pragma once | ||
|
|
||
| #include "boost/url/params_view.hpp" | ||
| #include "boost/url/segments_view.hpp" | ||
|
|
||
| #include <sstream> | ||
| #include <string_view> | ||
|
|
@@ -67,4 +68,20 @@ T parse_param(boost::urls::params_view const& params, | |
| return default_value.has_value() ? T{*default_value} : T{}; | ||
| } | ||
|
|
||
| } // namespace openapi | ||
| template <typename T> | ||
| T parse_segment(boost::urls::segments_view const& segs, | ||
| std::string_view name, | ||
| std::size_t idx) { | ||
| auto it = segs.begin(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it only needed in |
||
| if (idx < segs.size()) { | ||
| std::advance(it, idx); | ||
| auto v = T{}; | ||
| parse(*it, v); | ||
| return v; | ||
| } else { | ||
| throw bad_request_exception{ | ||
| fmt::format("missing segment parameter: {}", name)}; | ||
| } | ||
| } | ||
|
|
||
| } // namespace openapi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| #include <optional> | ||
| #include <ostream> | ||
| #include <ranges> | ||
|
|
||
| #include "utl/enumerate.h" | ||
|
|
||
|
|
@@ -108,7 +109,7 @@ std::string_view to_cpp(type const t) { | |
|
|
||
| struct indent { | ||
| explicit indent(int indent, char separator = ',') | ||
| : indent_{indent}, separator_{separator} {} | ||
| : separator_{separator}, indent_{indent} {} | ||
|
|
||
| void operator()(std::ostream& out) { | ||
| if (!first_ && separator_ != '\0') { | ||
|
|
@@ -314,10 +315,37 @@ void gen_member_init(YAML::Node const& root, | |
| out << ", allow_missing)}"; | ||
| } | ||
|
|
||
| void gen_member_init_from_seg(YAML::Node const& root, | ||
| YAML::Node const& x, | ||
| std::size_t idx, | ||
| std::ostream& out) { | ||
| auto const schema = x["schema"]; | ||
| auto const name = x["name"].as<std::string_view>(); | ||
| auto const type = get_type(root, name, schema, true); | ||
| out << " " << name << "_{::openapi::parse_segment<" << type << ">(segs, \"" | ||
| << name << "\", " << idx << ")}"; | ||
| } | ||
|
|
||
| void write_params(YAML::Node const& root, | ||
| YAML::Node const& path, | ||
| YAML::Node const& n, | ||
| std::ostream& header, | ||
| std::ostream& source) { | ||
| const auto p = path.as<std::string_view>(); | ||
| auto segs = p | std::views::split('/'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be inlined (not used afterwards and name is not telling anything) |
||
| auto seg_idx = std::unordered_map<std::string_view, std::size_t>{}; | ||
|
|
||
| for (auto const& [i, seg] : segs | std::views::enumerate) { | ||
| auto sv = std::string_view{seg.begin(), seg.end()}; | ||
| if (sv.starts_with('{')) { | ||
| sv.remove_prefix(1); | ||
| } | ||
| if (sv.ends_with('}')) { | ||
| sv.remove_suffix(1); | ||
| } | ||
| seg_idx.emplace(sv, i - 1); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0 - 1 underflows? |
||
| } | ||
|
|
||
| for (auto const& p : n["parameters"]) { | ||
| auto const name = p["name"].as<std::string_view>(); | ||
| auto const items = p["schema"]["items"]; | ||
|
|
@@ -333,20 +361,27 @@ void write_params(YAML::Node const& root, | |
| header << "struct " << id << " {\n"; | ||
| header << " explicit " << id << "();\n"; | ||
| header << " explicit " << id | ||
| << "(boost::urls::params_view const&, bool allow_missing = false);\n"; | ||
| << "(boost::urls::params_view const&, boost::urls::segments_view " | ||
| "const& = {}, bool allow_missing = false);\n"; | ||
| header << " boost::urls::url to_url(std::string_view path) const;\n"; | ||
|
|
||
| source << id << "::" << id << "() = default;\n"; | ||
| source << id << "::" << id | ||
| << "(boost::urls::params_view const& params, bool allow_missing)"; | ||
| << "(boost::urls::params_view const& params, " | ||
| "boost::urls::segments_view const& segs, bool allow_missing)"; | ||
|
|
||
| auto const parameters = n["parameters"]; | ||
| if (parameters.IsDefined() && parameters.size() != 0) { | ||
| source << " :"; | ||
| auto ind = indent{2}; | ||
| for (auto const& p : parameters) { | ||
| ind(source); | ||
| gen_member_init(root, p, is_required(p), source); | ||
| if (p["in"].IsDefined() && p["in"].as<std::string_view>() == "path") { | ||
| auto const idx = seg_idx.at(p["name"].as<std::string_view>()); | ||
| gen_member_init_from_seg(root, p, idx, source); | ||
| } else { | ||
| gen_member_init(root, p, is_required(p), source); | ||
| } | ||
| } | ||
| } | ||
| source << "\n {}\n\n"; | ||
|
|
@@ -357,6 +392,9 @@ void write_params(YAML::Node const& root, | |
| source << " auto u = boost::urls::url{path};\n"; | ||
| source << " auto default_val = " << id << "{};\n"; | ||
| for (auto const& p : parameters) { | ||
| if (p["in"].IsDefined() && p["in"].as<std::string_view>() == "path") { | ||
| continue; | ||
| } | ||
| auto const name = p["name"].as<std::string_view>(); | ||
| auto const schema = p["schema"]; | ||
| auto const has_default = schema["default"].IsDefined(); | ||
|
|
@@ -402,7 +440,9 @@ void write_params(YAML::Node const& root, | |
|
|
||
| for (auto const& p : n["parameters"]) { | ||
| auto const name = p["name"].as<std::string_view>(); | ||
| gen_member(root, name, is_required(p), p["schema"], header); | ||
| auto const in_path = | ||
| p["in"].IsDefined() && p["in"].as<std::string_view>() == "path"; | ||
|
Comment on lines
+443
to
+444
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't repeat yourself - write a utility function |
||
| gen_member(root, name, in_path || is_required(p), p["schema"], header); | ||
| } | ||
| header << "};\n\n"; | ||
| } | ||
|
|
@@ -560,7 +600,7 @@ void write_types(YAML::Node const& root, | |
|
|
||
| for (auto const& path : root["paths"]) { | ||
| for (auto const& method : path.second) { | ||
| write_params(root, method.second, header, source); | ||
| write_params(root, path.first, method.second, header, source); | ||
|
|
||
| for (auto const& response : method.second["responses"]) { | ||
| gen_type(method.second["operationId"].as<std::string>() + "_response", | ||
|
|
@@ -573,4 +613,4 @@ void write_types(YAML::Node const& root, | |
| write_postlude(header, source, ns); | ||
| } | ||
|
|
||
| } // namespace openapi | ||
| } // namespace openapi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #include "gtest/gtest.h" | ||
|
|
||
| #include "openapi/bad_request_exception.h" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. group headers |
||
| #include "yaml-cpp/yaml.h" | ||
|
|
||
| #include "cista/hash.h" | ||
|
|
||
| #include "boost/json.hpp" | ||
| #include "boost/url.hpp" | ||
|
|
||
| #include "utl/verify.h" | ||
|
|
||
| #include "openapi/gen_types.h" | ||
| #include "openapi/json.h" | ||
|
|
||
| #include "pet-api/pet-api.h" | ||
|
|
||
| using namespace openapi; | ||
| using namespace pet; | ||
|
|
||
| TEST(openapi, valid_query) { | ||
| auto url = boost::urls::url_view{"/items/test/42?param3=10"}; | ||
| auto p = pet::getItems_params{url.params(), url.segments()}; | ||
| EXPECT_EQ("test", p.param1_); | ||
| EXPECT_EQ(42, p.param2_); | ||
| EXPECT_EQ(10, p.param3_); | ||
| } | ||
|
|
||
| TEST(openapi, path_params_always_required) { | ||
| auto url = boost::urls::url_view{"/items?param3=10"}; | ||
| EXPECT_THROW(pet::getItems_params(url.params(), url.segments()), | ||
| openapi::bad_request_exception); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not used outside its own source file -> remove definition from header