| 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 |