comparison OrthancFramework/Sources/RestApi/RestApi.cpp @ 4405:5466f336b09f

gathering statistics about progress of api documentation
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 24 Dec 2020 09:37:30 +0100
parents ad646ff506d0
children 4cb9f66a1c7c
comparison
equal deleted inserted replaced
4404:f34634916d8c 4405:5466f336b09f
25 25
26 #include "../HttpServer/StringHttpOutput.h" 26 #include "../HttpServer/StringHttpOutput.h"
27 #include "../Logging.h" 27 #include "../Logging.h"
28 #include "../OrthancException.h" 28 #include "../OrthancException.h"
29 29
30 #include <boost/math/special_functions/round.hpp>
30 #include <stdlib.h> // To define "_exit()" under Windows 31 #include <stdlib.h> // To define "_exit()" under Windows
31 #include <stdio.h> 32 #include <stdio.h>
32 33
33 namespace Orthanc 34 namespace Orthanc
34 { 35 {
129 class OpenApiVisitor : public RestApiHierarchy::IVisitor 130 class OpenApiVisitor : public RestApiHierarchy::IVisitor
130 { 131 {
131 private: 132 private:
132 RestApi& restApi_; 133 RestApi& restApi_;
133 Json::Value paths_; 134 Json::Value paths_;
135 size_t successPathsCount_;
136 size_t totalPathsCount_;
134 137
135 public: 138 public:
136 explicit OpenApiVisitor(RestApi& restApi) : 139 explicit OpenApiVisitor(RestApi& restApi) :
137 restApi_(restApi) 140 restApi_(restApi),
141 paths_(Json::objectValue),
142 successPathsCount_(0),
143 totalPathsCount_(0)
138 { 144 {
139 } 145 }
140 146
141 virtual bool Visit(const RestApiHierarchy::Resource& resource, 147 virtual bool Visit(const RestApiHierarchy::Resource& resource,
142 const UriComponents& uri, 148 const UriComponents& uri,
143 bool hasTrailing, 149 bool hasTrailing,
144 const HttpToolbox::Arguments& components, 150 const HttpToolbox::Arguments& components,
145 const UriComponents& trailing) 151 const UriComponents& trailing)
146 { 152 {
147 const std::string path = Toolbox::FlattenUri(uri); 153 std::string path = Toolbox::FlattenUri(uri);
154 if (hasTrailing)
155 {
156 path += "/{...}";
157 }
148 158
149 if (paths_.isMember(path)) 159 if (paths_.isMember(path))
150 { 160 {
151 throw OrthancException(ErrorCode_InternalError); 161 throw OrthancException(ErrorCode_InternalError);
152 } 162 }
158 { 168 {
159 assert(it->second.empty()); 169 assert(it->second.empty());
160 uriArguments.insert(it->first.c_str()); 170 uriArguments.insert(it->first.c_str());
161 } 171 }
162 172
173 if (hasTrailing)
174 {
175 uriArguments.insert("...");
176 }
177
163 if (resource.HasHandler(HttpMethod_Get)) 178 if (resource.HasHandler(HttpMethod_Get))
164 { 179 {
180 totalPathsCount_ ++;
181
165 StringHttpOutput o1; 182 StringHttpOutput o1;
166 HttpOutput o2(o1, false); 183 HttpOutput o2(o1, false);
167 RestApiOutput o3(o2, HttpMethod_Get); 184 RestApiOutput o3(o2, HttpMethod_Get);
168 RestApiGetCall call(o3, restApi_, RequestOrigin_Documentation, "" /* remote IP */, 185 RestApiGetCall call(o3, restApi_, RequestOrigin_Documentation, "" /* remote IP */,
169 "" /* username */, HttpToolbox::Arguments() /* HTTP headers */, 186 "" /* username */, HttpToolbox::Arguments() /* HTTP headers */,
187 } 204 }
188 205
189 if (ok) 206 if (ok)
190 { 207 {
191 paths_[path]["get"] = v; 208 paths_[path]["get"] = v;
209 successPathsCount_ ++;
192 } 210 }
193 else 211 else
194 { 212 {
195 LOG(WARNING) << "Ignoring URI without API documentation: GET " << path; 213 LOG(WARNING) << "Ignoring URI without API documentation: GET " << path;
196 } 214 }
197 } 215 }
198 216
199 if (resource.HasHandler(HttpMethod_Post)) 217 if (resource.HasHandler(HttpMethod_Post))
200 { 218 {
219 totalPathsCount_ ++;
220
201 StringHttpOutput o1; 221 StringHttpOutput o1;
202 HttpOutput o2(o1, false); 222 HttpOutput o2(o1, false);
203 RestApiOutput o3(o2, HttpMethod_Post); 223 RestApiOutput o3(o2, HttpMethod_Post);
204 RestApiPostCall call(o3, restApi_, RequestOrigin_Documentation, "" /* remote IP */, 224 RestApiPostCall call(o3, restApi_, RequestOrigin_Documentation, "" /* remote IP */,
205 "" /* username */, HttpToolbox::Arguments() /* HTTP headers */, 225 "" /* username */, HttpToolbox::Arguments() /* HTTP headers */,
222 } 242 }
223 243
224 if (ok) 244 if (ok)
225 { 245 {
226 paths_[path]["post"] = v; 246 paths_[path]["post"] = v;
247 successPathsCount_ ++;
227 } 248 }
228 else 249 else
229 { 250 {
230 LOG(WARNING) << "Ignoring URI without API documentation: POST " << path; 251 LOG(WARNING) << "Ignoring URI without API documentation: POST " << path;
231 } 252 }
232 } 253 }
233 254
234 if (resource.HasHandler(HttpMethod_Delete)) 255 if (resource.HasHandler(HttpMethod_Delete))
235 { 256 {
257 totalPathsCount_ ++;
258
236 StringHttpOutput o1; 259 StringHttpOutput o1;
237 HttpOutput o2(o1, false); 260 HttpOutput o2(o1, false);
238 RestApiOutput o3(o2, HttpMethod_Delete); 261 RestApiOutput o3(o2, HttpMethod_Delete);
239 RestApiDeleteCall call(o3, restApi_, RequestOrigin_Documentation, "" /* remote IP */, 262 RestApiDeleteCall call(o3, restApi_, RequestOrigin_Documentation, "" /* remote IP */,
240 "" /* username */, HttpToolbox::Arguments() /* HTTP headers */, 263 "" /* username */, HttpToolbox::Arguments() /* HTTP headers */,
257 } 280 }
258 281
259 if (ok) 282 if (ok)
260 { 283 {
261 paths_[path]["delete"] = v; 284 paths_[path]["delete"] = v;
285 successPathsCount_ ++;
262 } 286 }
263 else 287 else
264 { 288 {
265 LOG(WARNING) << "Ignoring URI without API documentation: DELETE " << path; 289 LOG(WARNING) << "Ignoring URI without API documentation: DELETE " << path;
266 } 290 }
267 } 291 }
268 292
269 if (resource.HasHandler(HttpMethod_Put)) 293 if (resource.HasHandler(HttpMethod_Put))
270 { 294 {
295 totalPathsCount_ ++;
296
271 StringHttpOutput o1; 297 StringHttpOutput o1;
272 HttpOutput o2(o1, false); 298 HttpOutput o2(o1, false);
273 RestApiOutput o3(o2, HttpMethod_Put); 299 RestApiOutput o3(o2, HttpMethod_Put);
274 RestApiPutCall call(o3, restApi_, RequestOrigin_Documentation, "" /* remote IP */, 300 RestApiPutCall call(o3, restApi_, RequestOrigin_Documentation, "" /* remote IP */,
275 "" /* username */, HttpToolbox::Arguments() /* HTTP headers */, 301 "" /* username */, HttpToolbox::Arguments() /* HTTP headers */,
292 } 318 }
293 319
294 if (ok) 320 if (ok)
295 { 321 {
296 paths_[path]["put"] = v; 322 paths_[path]["put"] = v;
323 successPathsCount_ ++;
297 } 324 }
298 else 325 else
299 { 326 {
300 LOG(WARNING) << "Ignoring URI without API documentation: PUT " << path; 327 LOG(WARNING) << "Ignoring URI without API documentation: PUT " << path;
301 } 328 }
306 333
307 334
308 const Json::Value& GetPaths() const 335 const Json::Value& GetPaths() const
309 { 336 {
310 return paths_; 337 return paths_;
338 }
339
340 size_t GetSuccessPathsCount() const
341 {
342 return successPathsCount_;
343 }
344
345 size_t GetTotalPathsCount() const
346 {
347 return totalPathsCount_;
311 } 348 }
312 }; 349 };
313 } 350 }
314 351
315 352
493 530
494 target["info"] = Json::objectValue; 531 target["info"] = Json::objectValue;
495 target["openapi"] = "3.0.0"; 532 target["openapi"] = "3.0.0";
496 target["servers"] = Json::arrayValue; 533 target["servers"] = Json::arrayValue;
497 target["paths"] = visitor.GetPaths(); 534 target["paths"] = visitor.GetPaths();
535
536 assert(visitor.GetSuccessPathsCount() <= visitor.GetTotalPathsCount());
537 size_t total = visitor.GetTotalPathsCount();
538 if (total == 0)
539 {
540 total = 1; // Avoid division by zero
541 }
542 float coverage = (100.0f * static_cast<float>(visitor.GetSuccessPathsCount()) /
543 static_cast<float>(total));
544
545 LOG(WARNING) << "The documentation of the REST API contains " << visitor.GetSuccessPathsCount()
546 << " paths over a total of " << visitor.GetTotalPathsCount() << " paths "
547 << "(" << static_cast<unsigned int>(boost::math::iround(coverage)) << "%)";
498 } 548 }
499 } 549 }