Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2024 Muhammad Nawaz | ||
3 | * Licensed under the MIT License. See LICENSE file for more information. | ||
4 | */ | ||
5 | // [ END OF LICENSE c6bd0f49d040fca8d8a9cb05868e66aa63f0e2e0 ] | ||
6 | |||
7 | #include "oas_validator_imp.hpp" | ||
8 | #include <algorithm> | ||
9 | #include <fstream> | ||
10 | #include <rapidjson/istreamwrapper.h> | ||
11 | #include <sstream> | ||
12 | |||
13 | 7 | OASValidatorImp::OASValidatorImp(const std::string& oas_specs, | |
14 | 7 | const std::unordered_map<std::string, std::unordered_set<std::string>>& method_map) | |
15 |
10/20✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 7 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 7 times.
✗ Branch 16 not taken.
✓ Branch 19 taken 7 times.
✗ Branch 20 not taken.
✓ Branch 23 taken 7 times.
✗ Branch 24 not taken.
✓ Branch 27 taken 7 times.
✗ Branch 28 not taken.
✓ Branch 31 taken 7 times.
✗ Branch 32 not taken.
✓ Branch 35 taken 7 times.
✗ Branch 36 not taken.
✓ Branch 38 taken 7 times.
✗ Branch 39 not taken.
|
7 | : method_map_(BuildCaseInsensitiveMap(method_map)) |
16 | { | ||
17 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | rapidjson::Document doc; |
18 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 times.
|
7 | ParseSpecs(oas_specs, doc); |
19 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | ResolveReferences(doc, doc, doc.GetAllocator()); |
20 | |||
21 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | const rapidjson::Value& paths = doc["paths"]; |
22 | 6 | std::vector<std::string> ref_keys; | |
23 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | ref_keys.emplace_back("paths"); |
24 | |||
25 |
4/6✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 534 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 528 times.
✓ Branch 9 taken 6 times.
|
534 | for (auto path_itr = paths.MemberBegin(); path_itr != paths.MemberEnd(); ++path_itr) { |
26 |
1/2✓ Branch 1 taken 528 times.
✗ Branch 2 not taken.
|
528 | ProcessPath(path_itr, ref_keys); |
27 | } | ||
28 | 10 | } | |
29 | |||
30 | 12 | ValidationError OASValidatorImp::ValidateRoute(const std::string& method, const std::string& http_path, | |
31 | std::string& error_msg) | ||
32 | { | ||
33 | ValidatorsStore* validators; | ||
34 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
24 | return GetValidators(method, http_path, validators, error_msg); |
35 | } | ||
36 | |||
37 | 4 | ValidationError OASValidatorImp::ValidateBody(const std::string& method, const std::string& http_path, | |
38 | const std::string& json_body, std::string& error_msg) | ||
39 | { | ||
40 | ValidatorsStore* validators; | ||
41 | |||
42 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | auto err_code = GetValidators(method, http_path, validators, error_msg); |
43 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | CHECK_ERROR(err_code) |
44 | |||
45 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | return validators->ValidateBody(json_body, error_msg); |
46 | } | ||
47 | |||
48 | 12 | ValidationError OASValidatorImp::ValidatePathParam(const std::string& method, const std::string& http_path, | |
49 | std::string& error_msg) | ||
50 | { | ||
51 | 12 | std::unordered_map<size_t, ParamRange> param_idxs; | |
52 | ValidatorsStore* validators; | ||
53 | |||
54 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | auto err_code = GetValidators(method, http_path, validators, error_msg, ¶m_idxs); |
55 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | CHECK_ERROR(err_code) |
56 | |||
57 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | return validators->ValidatePathParams(param_idxs, error_msg); |
58 | 12 | } | |
59 | |||
60 | 6 | ValidationError OASValidatorImp::ValidateQueryParam(const std::string& method, const std::string& http_path, | |
61 | std::string& error_msg) | ||
62 | { | ||
63 | 6 | std::string query; | |
64 | ValidatorsStore* validators; | ||
65 | |||
66 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | auto err_code = GetValidators(method, http_path, validators, error_msg, nullptr, &query); |
67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | CHECK_ERROR(err_code) |
68 | |||
69 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | return validators->ValidateQueryParams(query, error_msg); |
70 | 6 | } | |
71 | |||
72 | 6 | ValidationError OASValidatorImp::ValidateHeaders(const std::string& method, const std::string& http_path, | |
73 | const std::unordered_map<std::string, std::string>& headers, | ||
74 | std::string& error_msg) | ||
75 | { | ||
76 | ValidatorsStore* validators; | ||
77 | |||
78 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | auto err_code = GetValidators(method, http_path, validators, error_msg); |
79 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | CHECK_ERROR(err_code) |
80 | |||
81 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | return validators->ValidateHeaderParams(headers, error_msg); |
82 | } | ||
83 | |||
84 | 17 | ValidationError OASValidatorImp::ValidateRequest(const std::string& method, const std::string& http_path, | |
85 | std::string& error_msg) | ||
86 | { | ||
87 | 17 | std::unordered_map<size_t, ParamRange> param_idxs; | |
88 | 17 | std::string query; | |
89 | ValidatorsStore* validators; | ||
90 | |||
91 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | auto err_code = GetValidators(method, http_path, validators, error_msg, ¶m_idxs, &query); |
92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | CHECK_ERROR(err_code) |
93 | |||
94 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | err_code = validators->ValidatePathParams(param_idxs, error_msg); |
95 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 10 times.
|
17 | CHECK_ERROR(err_code) |
96 | |||
97 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | return validators->ValidateQueryParams(query, error_msg); |
98 | 17 | } | |
99 | |||
100 | 1 | ValidationError OASValidatorImp::ValidateRequest(const std::string& method, const std::string& http_path, | |
101 | const std::string& json_body, std::string& error_msg) | ||
102 | { | ||
103 | 1 | std::unordered_map<size_t, ParamRange> param_idxs; | |
104 | 1 | std::string query; | |
105 | ValidatorsStore* validators; | ||
106 | |||
107 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | auto err_code = GetValidators(method, http_path, validators, error_msg, ¶m_idxs, &query); |
108 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CHECK_ERROR(err_code) |
109 | |||
110 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | err_code = validators->ValidateBody(json_body, error_msg); |
111 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CHECK_ERROR(err_code) |
112 | |||
113 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | err_code = validators->ValidatePathParams(param_idxs, error_msg); |
114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CHECK_ERROR(err_code) |
115 | |||
116 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | return validators->ValidateQueryParams(query, error_msg); |
117 | 1 | } | |
118 | |||
119 | 3 | ValidationError OASValidatorImp::ValidateRequest(const std::string& method, const std::string& http_path, | |
120 | const std::unordered_map<std::string, std::string>& headers, | ||
121 | std::string& error_msg) | ||
122 | { | ||
123 | 3 | std::unordered_map<size_t, ParamRange> param_idxs; | |
124 | 3 | std::string query; | |
125 | ValidatorsStore* validators; | ||
126 | |||
127 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | auto err_code = GetValidators(method, http_path, validators, error_msg, ¶m_idxs, &query); |
128 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | CHECK_ERROR(err_code) |
129 | |||
130 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | err_code = validators->ValidatePathParams(param_idxs, error_msg); |
131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CHECK_ERROR(err_code) |
132 | |||
133 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | err_code = validators->ValidateQueryParams(query, error_msg); |
134 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CHECK_ERROR(err_code) |
135 | |||
136 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | return validators->ValidateHeaderParams(headers, error_msg); |
137 | 3 | } | |
138 | |||
139 | 3 | ValidationError OASValidatorImp::ValidateRequest(const std::string& method, const std::string& http_path, | |
140 | const std::string& json_body, | ||
141 | const std::unordered_map<std::string, std::string>& headers, | ||
142 | std::string& error_msg) | ||
143 | { | ||
144 | 3 | std::unordered_map<size_t, ParamRange> param_idxs; | |
145 | 3 | std::string query; | |
146 | ValidatorsStore* validators; | ||
147 | |||
148 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | auto err_code = GetValidators(method, http_path, validators, error_msg, ¶m_idxs, &query); |
149 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | CHECK_ERROR(err_code) |
150 | |||
151 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | err_code = validators->ValidateBody(json_body, error_msg); |
152 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | CHECK_ERROR(err_code) |
153 | |||
154 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | err_code = validators->ValidatePathParams(param_idxs, error_msg); |
155 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | CHECK_ERROR(err_code) |
156 | |||
157 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | err_code = validators->ValidateQueryParams(query, error_msg); |
158 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | CHECK_ERROR(err_code) |
159 | |||
160 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | return validators->ValidateHeaderParams(headers, error_msg); |
161 | 3 | } | |
162 | |||
163 | 12 | OASValidatorImp::~OASValidatorImp() | |
164 | { | ||
165 | #ifndef LUA_OAS_VALIDATOR // LUA manages garbage collection itself | ||
166 |
2/2✓ Branch 2 taken 54 times.
✓ Branch 3 taken 6 times.
|
60 | for (auto& per_method_validator : oas_validators_) { |
167 |
2/2✓ Branch 5 taken 528 times.
✓ Branch 6 taken 54 times.
|
582 | for (auto& per_path_validator : per_method_validator.per_path_validators) { |
168 |
1/2✓ Branch 0 taken 528 times.
✗ Branch 1 not taken.
|
528 | delete per_path_validator.second; |
169 | } | ||
170 | } | ||
171 | #endif | ||
172 | 6 | } | |
173 | |||
174 | 64 | ValidationError OASValidatorImp::GetValidators(const std::string& method, const std::string& http_path, | |
175 | ValidatorsStore*& validators, std::string& error_msg, | ||
176 | std::unordered_map<size_t, ParamRange>* param_idxs, std::string* query) | ||
177 | { | ||
178 | 64 | auto err_code = method_validator_.Validate(method, error_msg); | |
179 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 63 times.
|
64 | CHECK_ERROR(err_code) |
180 | |||
181 | 63 | err_code = GetValidators(method, method, http_path, validators, error_msg, param_idxs, query); | |
182 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 62 times.
|
63 | if (ValidationError::INVALID_ROUTE == err_code) { |
183 | try { | ||
184 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
1 | auto mapped_methods = method_map_.at(method); |
185 | ✗ | for (const auto& mapped_method : mapped_methods) { | |
186 | ✗ | err_code = GetValidators(method, mapped_method, http_path, validators, error_msg, param_idxs, query); | |
187 | ✗ | if (ValidationError::NONE == err_code) { | |
188 | ✗ | return err_code; | |
189 | } | ||
190 | } | ||
191 |
1/4✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
|
1 | } catch (const std::out_of_range&) { |
192 | 1 | return err_code; | |
193 | 1 | } | |
194 | } | ||
195 | |||
196 | 62 | return err_code; | |
197 | } | ||
198 | |||
199 | 63 | ValidationError OASValidatorImp::GetValidators(const std::string& method, const std::string& mapped_method, | |
200 | const std::string& http_path, ValidatorsStore*& validators, | ||
201 | std::string& error_msg, | ||
202 | std::unordered_map<size_t, ParamRange>* param_idxs, std::string* query) | ||
203 | { | ||
204 | 63 | auto enum_method = kStringToMethod.at(mapped_method); | |
205 | |||
206 | 63 | auto query_pos = http_path.find('?'); | |
207 |
3/4✓ Branch 0 taken 11 times.
✓ Branch 1 taken 52 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
63 | if (std::string::npos != query_pos && query) { |
208 | 11 | *query = http_path.substr(query_pos); | |
209 | } | ||
210 | |||
211 | try { | ||
212 | // 1st. try, no path params | ||
213 |
2/2✓ Branch 2 taken 20 times.
✓ Branch 3 taken 43 times.
|
126 | validators = oas_validators_[static_cast<size_t>(enum_method)].per_path_validators.at( |
214 |
4/6✓ Branch 0 taken 52 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 52 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
|
169 | std::string::npos == query_pos ? http_path : http_path.substr(0, query_pos)); |
215 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
|
43 | } catch (const std::out_of_range&) { |
216 | // 2nd try, if path has dynamic path parameters | ||
217 | 43 | std::string map_key; | |
218 |
1/2✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
|
43 | map_key.reserve(http_path.length() + 32); |
219 | 43 | const char* beg = http_path.c_str(); | |
220 |
2/2✓ Branch 1 taken 40 times.
✓ Branch 2 taken 3 times.
|
43 | const char* const end = http_path.c_str() + (std::string::npos == query_pos ? http_path.length() : query_pos); |
221 |
3/4✓ Branch 0 taken 31 times.
✓ Branch 1 taken 12 times.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
|
43 | bool found = param_idxs ? oas_validators_[static_cast<size_t>(enum_method)].path_trie.Search(beg, end, map_key, |
222 | *param_idxs) | ||
223 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | : oas_validators_[static_cast<size_t>(enum_method)].path_trie.Search(beg, end, map_key); |
224 |
2/2✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1 times.
|
43 | if (found) { |
225 | try { | ||
226 |
1/2✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
|
42 | validators = oas_validators_[static_cast<size_t>(enum_method)].per_path_validators.at(map_key); |
227 | ✗ | } catch (const std::out_of_range&) { | |
228 | ✗ | error_msg = R"({"errorCode":"INVALID_ROUTE","details":{"description": "Invalid HTTP method ')" + | |
229 | ✗ | method + "' or path: '" + http_path + R"('"}})"; | |
230 | ✗ | return ValidationError::INVALID_ROUTE; | |
231 | ✗ | } | |
232 | } else { | ||
233 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | error_msg = R"({"errorCode":"INVALID_ROUTE","details":{"description": "Invalid HTTP method ')" + method + |
234 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | "' or path: '" + http_path + R"('"}})"; |
235 | 1 | return ValidationError::INVALID_ROUTE; | |
236 | } | ||
237 |
4/4✓ Branch 1 taken 42 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 42 times.
|
86 | } |
238 | 62 | return ValidationError::NONE; | |
239 | } | ||
240 | |||
241 | ✗ | std::vector<std::string> OASValidatorImp::Split(const std::string& str) | |
242 | { | ||
243 | ✗ | std::vector<std::string> tokens; | |
244 | ✗ | std::string token; | |
245 | ✗ | std::istringstream token_stream(str); | |
246 | ✗ | while (getline(token_stream, token, '/')) { | |
247 | ✗ | tokens.push_back(token); | |
248 | } | ||
249 | ✗ | return tokens; | |
250 | ✗ | } | |
251 | |||
252 | ✗ | rapidjson::Value* OASValidatorImp::ResolvePath(rapidjson::Document& doc, const std::string& path) | |
253 | { | ||
254 | ✗ | std::vector<std::string> parts = Split(path); | |
255 | ✗ | rapidjson::Value* current = &doc; | |
256 | ✗ | for (const std::string& part : parts) { | |
257 | ✗ | if (!current->IsObject() || !current->HasMember(part.c_str())) { | |
258 | ✗ | throw ValidatorInitExc("Invalid path or missing member:" + part); | |
259 | } | ||
260 | ✗ | current = &(*current)[part.c_str()]; | |
261 | } | ||
262 | ✗ | return current; | |
263 | ✗ | } | |
264 | |||
265 | 7 | void OASValidatorImp::ParseSpecs(const std::string& oas_specs, rapidjson::Document& doc) | |
266 | { | ||
267 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | std::ifstream ifs(oas_specs); |
268 | |||
269 |
3/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 1 times.
|
7 | if (ifs.is_open()) { |
270 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | rapidjson::IStreamWrapper isw(ifs); |
271 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | doc.ParseStream(isw); |
272 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | ifs.close(); |
273 | } else { | ||
274 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | doc.Parse(oas_specs.c_str()); |
275 | } | ||
276 | |||
277 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
|
7 | if (doc.HasParseError()) { |
278 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
3 | throw ValidatorInitExc("Unable to parse specs: " + oas_specs + |
279 |
3/6✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
4 | " \nError code: " + std::to_string(doc.GetParseError()) + |
280 |
3/6✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
4 | " at offset: " + std::to_string(doc.GetErrorOffset()) + |
281 |
1/2✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
4 | " Error message: " + rapidjson::GetParseError_En(doc.GetParseError())); |
282 | } | ||
283 | 7 | } | |
284 | |||
285 | 528 | void OASValidatorImp::ProcessPath(const rapidjson::Value::ConstMemberIterator& path_itr, | |
286 | std::vector<std::string>& ref_keys) | ||
287 | { | ||
288 |
2/4✓ Branch 3 taken 528 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 528 times.
✗ Branch 7 not taken.
|
528 | std::string path(path_itr->name.GetString()); |
289 |
2/4✓ Branch 1 taken 528 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 528 times.
✗ Branch 5 not taken.
|
528 | ref_keys.emplace_back(EscapeSlash(path)); |
290 | 528 | const rapidjson::Value& methods = path_itr->value; | |
291 | |||
292 |
4/6✓ Branch 1 taken 528 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1056 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 528 times.
✓ Branch 9 taken 528 times.
|
1056 | for (auto method_itr = methods.MemberBegin(); method_itr != methods.MemberEnd(); ++method_itr) { |
293 |
1/2✓ Branch 1 taken 528 times.
✗ Branch 2 not taken.
|
528 | ProcessMethod(method_itr, path, ref_keys); |
294 | } | ||
295 | |||
296 | 528 | ref_keys.pop_back(); // Pop the path key | |
297 | 528 | } | |
298 | |||
299 | 528 | void OASValidatorImp::ProcessMethod(const rapidjson::Value::ConstMemberIterator& method_itr, const std::string& path, | |
300 | std::vector<std::string>& ref_keys) | ||
301 | { | ||
302 |
1/2✓ Branch 3 taken 528 times.
✗ Branch 4 not taken.
|
528 | ref_keys.emplace_back(method_itr->name.GetString()); |
303 |
3/6✓ Branch 3 taken 528 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 528 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 528 times.
✗ Branch 10 not taken.
|
528 | auto method(kStringToMethod.at(method_itr->name.GetString())); |
304 | 528 | auto& per_method_validator = oas_validators_[static_cast<size_t>(method)]; | |
305 | 528 | auto& per_path_validator = per_method_validator.per_path_validators; | |
306 | |||
307 | 528 | ProcessRequestBody(method_itr, path, ref_keys, per_path_validator); | |
308 | 528 | ProcessParameters(method_itr, path, ref_keys, per_path_validator); | |
309 | |||
310 |
5/6✓ Branch 1 taken 186 times.
✓ Branch 2 taken 342 times.
✓ Branch 4 taken 186 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 186 times.
✓ Branch 7 taken 342 times.
|
528 | if (std::string::npos != path.find('{') && std::string::npos != path.find('}')) { // has path params |
311 | 186 | per_method_validator.path_trie.Insert(path); | |
312 | } | ||
313 | |||
314 | 528 | ref_keys.pop_back(); // Pop the method key | |
315 | 528 | } | |
316 | |||
317 | 528 | void OASValidatorImp::ProcessRequestBody(const rapidjson::Value::ConstMemberIterator& method_itr, | |
318 | const std::string& path, std::vector<std::string>& ref_keys, | ||
319 | std::unordered_map<std::string, ValidatorsStore*>& per_path_validator) | ||
320 | { | ||
321 |
1/2✓ Branch 5 taken 126 times.
✗ Branch 6 not taken.
|
654 | if ((method_itr->value.HasMember("requestBody")) && (method_itr->value["requestBody"].HasMember("content")) && |
322 |
5/6✓ Branch 0 taken 126 times.
✓ Branch 1 taken 402 times.
✓ Branch 6 taken 126 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 126 times.
✓ Branch 9 taken 402 times.
|
780 | (method_itr->value["requestBody"]["content"].HasMember("application/json")) && |
323 |
1/2✓ Branch 5 taken 126 times.
✗ Branch 6 not taken.
|
126 | (method_itr->value["requestBody"]["content"]["application/json"].HasMember( |
324 | "schema"))) { // if "method+path" has json body | ||
325 | 126 | ref_keys.emplace_back("requestBody/content/application%2Fjson/schema"); | |
326 | 126 | per_path_validator.emplace( | |
327 | path, | ||
328 |
2/4✓ Branch 7 taken 126 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 126 times.
✗ Branch 11 not taken.
|
126 | new ValidatorsStore(method_itr->value["requestBody"]["content"]["application/json"]["schema"], ref_keys)); |
329 | 126 | ref_keys.pop_back(); // pop body ref | |
330 | } else { // Otherwise validators without body | ||
331 |
1/2✓ Branch 3 taken 402 times.
✗ Branch 4 not taken.
|
402 | per_path_validator.emplace(path, new ValidatorsStore()); |
332 | } | ||
333 | 528 | } | |
334 | |||
335 | 528 | void OASValidatorImp::ProcessParameters(const rapidjson::Value::ConstMemberIterator& method_itr, | |
336 | const std::string& path, std::vector<std::string>& ref_keys, | ||
337 | std::unordered_map<std::string, ValidatorsStore*>& per_path_validator) | ||
338 | { | ||
339 |
2/2✓ Branch 2 taken 396 times.
✓ Branch 3 taken 132 times.
|
528 | if (method_itr->value.HasMember("parameters")) { // if "method+path" has parameters |
340 | 396 | ref_keys.emplace_back("parameters"); | |
341 | 396 | per_path_validator.at(path)->AddParamValidators(path, method_itr->value["parameters"], ref_keys); | |
342 | 396 | ref_keys.pop_back(); | |
343 | } | ||
344 | 528 | } | |
345 | |||
346 | 11484 | void OASValidatorImp::ResolveReferences(rapidjson::Value& value, rapidjson::Document& doc, | |
347 | rapidjson::Document::AllocatorType& allocator) | ||
348 | { | ||
349 |
2/2✓ Branch 1 taken 5346 times.
✓ Branch 2 taken 6138 times.
|
11484 | if (value.IsObject()) { |
350 | bool should_reiterate; | ||
351 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5346 times.
|
5346 | do { |
352 | 5346 | should_reiterate = false; | |
353 |
4/6✓ Branch 1 taken 5346 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15978 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10632 times.
✓ Branch 8 taken 5346 times.
|
15978 | for (auto itr = value.MemberBegin(); itr != value.MemberEnd();) { |
354 |
3/8✓ Branch 2 taken 10632 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10632 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 10632 times.
|
10632 | if (strcmp(itr->name.GetString(), "$ref") == 0 && itr->value.IsString()) { |
355 | ✗ | std::string ref = itr->value.GetString(); | |
356 | ✗ | if (ref.rfind("#/", 0) == 0) { | |
357 | ✗ | ref.erase(0, 2); // Remove '#/' | |
358 | ✗ | rapidjson::Value const* ref_value = ResolvePath(doc, ref); | |
359 | ✗ | if (ref_value) { | |
360 | ✗ | value.CopyFrom(*ref_value, allocator); | |
361 | ✗ | should_reiterate = true; // Signal to reiterate due to structural changes | |
362 | ✗ | break; // Exit the loop to avoid invalid iterator use | |
363 | } | ||
364 | } | ||
365 | ✗ | } else { | |
366 |
1/2✓ Branch 2 taken 10632 times.
✗ Branch 3 not taken.
|
10632 | ResolveReferences(itr->value, doc, allocator); |
367 | } | ||
368 | 10632 | ++itr; | |
369 | } | ||
370 | } while (should_reiterate); // Keep resolving until no more references are found | ||
371 |
2/2✓ Branch 1 taken 516 times.
✓ Branch 2 taken 5622 times.
|
6138 | } else if (value.IsArray()) { |
372 |
2/2✓ Branch 1 taken 846 times.
✓ Branch 2 taken 516 times.
|
1362 | for (rapidjson::SizeType i = 0; i < value.Size(); i++) { |
373 | 846 | ResolveReferences(value[i], doc, allocator); | |
374 | } | ||
375 | } | ||
376 | 11484 | } | |
377 | |||
378 | 7 | std::unordered_map<std::string, std::unordered_set<std::string>> OASValidatorImp::BuildCaseInsensitiveMap( | |
379 | const std::unordered_map<std::string, std::unordered_set<std::string>>& method_map) | ||
380 | { | ||
381 | |||
382 | 7 | std::unordered_map<std::string, std::unordered_set<std::string>> case_insensitive_map; | |
383 | |||
384 |
1/2✗ Branch 5 not taken.
✓ Branch 6 taken 7 times.
|
7 | for (const auto& entry : method_map) { |
385 | ✗ | std::string lower_key(entry.first); | |
386 | ✗ | std::transform(lower_key.begin(), lower_key.end(), lower_key.begin(), ::tolower); | |
387 | ✗ | std::string upper_key(entry.first); | |
388 | ✗ | std::transform(upper_key.begin(), upper_key.end(), upper_key.begin(), ::toupper); | |
389 | ✗ | case_insensitive_map[lower_key] = entry.second; | |
390 | ✗ | case_insensitive_map[upper_key] = entry.second; | |
391 | ✗ | } | |
392 | |||
393 | 7 | return case_insensitive_map; | |
394 | ✗ | } | |
395 | |||
396 | const std::unordered_map<std::string, HttpMethod> OASValidatorImp::kStringToMethod = { | ||
397 | {"GET", HttpMethod::GET}, {"POST", HttpMethod::POST}, {"PUT", HttpMethod::PUT}, | ||
398 | {"DELETE", HttpMethod::DELETE}, {"HEAD", HttpMethod::HEAD}, {"OPTIONS", HttpMethod::OPTIONS}, | ||
399 | {"PATCH", HttpMethod::PATCH}, {"CONNECT", HttpMethod::CONNECT}, {"TRACE", HttpMethod::TRACE}, | ||
400 | {"get", HttpMethod::GET}, {"post", HttpMethod::POST}, {"put", HttpMethod::PUT}, | ||
401 | {"delete", HttpMethod::DELETE}, {"head", HttpMethod::HEAD}, {"options", HttpMethod::OPTIONS}, | ||
402 | {"patch", HttpMethod::PATCH}, {"connect", HttpMethod::CONNECT}, {"trace", HttpMethod::TRACE}}; | ||
403 |