Mercurial > hg > orthanc
comparison UnitTestsSources/RestApiTests.cpp @ 968:8ed284e79850
RestApiHierarchy
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 27 Jun 2014 17:48:55 +0200 |
parents | dfc076546821 |
children | 3dce528b0cc2 |
comparison
equal
deleted
inserted
replaced
967:dfc076546821 | 968:8ed284e79850 |
---|---|
192 | 192 |
193 | 193 |
194 | 194 |
195 namespace Orthanc | 195 namespace Orthanc |
196 { | 196 { |
197 class RestApiResource | 197 class RestApiHierarchy |
198 { | 198 { |
199 private: | 199 private: |
200 struct Handlers | 200 struct Handlers |
201 { | 201 { |
202 std::list<RestApi::GetHandler> getHandlers_; | 202 typedef std::list<RestApi::GetHandler> GetHandlers; |
203 std::list<RestApi::PutHandler> putHandlers_; | 203 typedef std::list<RestApi::PostHandler> PostHandlers; |
204 std::list<RestApi::PostHandler> postHandlers_; | 204 typedef std::list<RestApi::PutHandler> PutHandlers; |
205 std::list<RestApi::DeleteHandler> deleteHandlers_; | 205 typedef std::list<RestApi::DeleteHandler> DeleteHandlers; |
206 | |
207 GetHandlers getHandlers_; | |
208 PostHandlers postHandlers_; | |
209 PutHandlers putHandlers_; | |
210 DeleteHandlers deleteHandlers_; | |
211 | |
212 bool HasGet() const | |
213 { | |
214 return getHandlers_.size() > 0; | |
215 } | |
206 | 216 |
207 void Register(RestApi::GetHandler handler) | 217 void Register(RestApi::GetHandler handler) |
208 { | 218 { |
209 getHandlers_.push_back(handler); | 219 getHandlers_.push_back(handler); |
210 } | 220 } |
221 | 231 |
222 void Register(RestApi::DeleteHandler handler) | 232 void Register(RestApi::DeleteHandler handler) |
223 { | 233 { |
224 deleteHandlers_.push_back(handler); | 234 deleteHandlers_.push_back(handler); |
225 } | 235 } |
236 | |
237 bool IsEmpty() const | |
238 { | |
239 return (getHandlers_.empty() && | |
240 postHandlers_.empty() && | |
241 putHandlers_.empty() && | |
242 deleteHandlers_.empty()); | |
243 } | |
226 }; | 244 }; |
227 | 245 |
228 | 246 |
229 typedef std::map<std::string, RestApiResource*> Children; | 247 typedef std::map<std::string, RestApiHierarchy*> Children; |
230 | 248 typedef bool (*ResourceCallback) (Handlers&, |
249 const UriComponents& uri, | |
250 const RestApiPath::Components& components, | |
251 const UriComponents& trailing, | |
252 void* call); | |
253 | |
254 Handlers handlers_; | |
231 Children children_; | 255 Children children_; |
232 Children wildcardChildren_; | 256 Children wildcardChildren_; |
233 Handlers handlers_; | |
234 Handlers universalHandlers_; | 257 Handlers universalHandlers_; |
235 | 258 |
236 | 259 |
237 static RestApiResource& AddChild(Children& children, | 260 static RestApiHierarchy& AddChild(Children& children, |
238 const std::string& name) | 261 const std::string& name) |
239 { | 262 { |
240 Children::iterator it = children.find(name); | 263 Children::iterator it = children.find(name); |
241 | 264 |
242 if (it == children.end()) | 265 if (it == children.end()) |
243 { | 266 { |
244 // Create new child | 267 // Create new child |
245 RestApiResource *child = new RestApiResource; | 268 RestApiHierarchy *child = new RestApiHierarchy; |
246 children[name] = child; | 269 children[name] = child; |
247 return *child; | 270 return *child; |
248 } | 271 } |
249 else | 272 else |
250 { | 273 { |
259 it != children.end(); it++) | 282 it != children.end(); it++) |
260 { | 283 { |
261 delete it->second; | 284 delete it->second; |
262 } | 285 } |
263 } | 286 } |
264 | |
265 | |
266 | 287 |
267 | 288 |
268 template <typename Handler> | 289 template <typename Handler> |
269 void RegisterInternal(const RestApiPath& path, | 290 void RegisterInternal(const RestApiPath& path, |
270 Handler handler, | 291 Handler handler, |
279 else | 300 else |
280 { | 301 { |
281 handlers_.Register(handler); | 302 handlers_.Register(handler); |
282 } | 303 } |
283 } | 304 } |
284 else if (path.IsWildcardLevel(level)) | 305 else |
285 { | 306 { |
286 AddChild(wildcardChildren_, path.GetWildcardName(level)); | 307 RestApiHierarchy* child; |
287 } | 308 if (path.IsWildcardLevel(level)) |
309 { | |
310 child = &AddChild(wildcardChildren_, path.GetWildcardName(level)); | |
311 } | |
312 else | |
313 { | |
314 child = &AddChild(children_, path.GetLevelName(level)); | |
315 } | |
316 | |
317 child->RegisterInternal(path, handler, level + 1); | |
318 } | |
319 } | |
320 | |
321 | |
322 bool LookupHandler(RestApiPath::Components& components, | |
323 const UriComponents& uri, | |
324 ResourceCallback callback, | |
325 size_t level, | |
326 void* call) | |
327 { | |
328 assert(uri.size() >= level); | |
329 UriComponents trailing; | |
330 | |
331 // Look for an exact match on the resource of interest | |
332 if (uri.size() == level) | |
333 { | |
334 if (!handlers_.IsEmpty() && | |
335 callback(handlers_, uri, components, trailing, call)) | |
336 { | |
337 return true; | |
338 } | |
339 } | |
340 | |
341 | |
342 // Try and go down in the hierarchy, using an exact match for the child | |
343 Children::const_iterator child = children_.find(uri[level]); | |
344 if (child != children_.end()) | |
345 { | |
346 if (child->second->LookupHandler(components, uri, callback, level + 1, call)) | |
347 { | |
348 return true; | |
349 } | |
350 } | |
351 | |
352 | |
353 // Try and go down in the hierarchy, using wildcard rules for children | |
354 for (child = wildcardChildren_.begin(); | |
355 child != wildcardChildren_.end(); child++) | |
356 { | |
357 RestApiPath::Components subComponents = components; | |
358 subComponents[child->first] = uri[level]; | |
359 | |
360 if (child->second->LookupHandler(components, uri, callback, level + 1, call)) | |
361 { | |
362 return true; | |
363 } | |
364 } | |
365 | |
366 | |
367 // As a last resort, call the universal handlers, if any | |
368 if (!universalHandlers_.IsEmpty()) | |
369 { | |
370 trailing.resize(uri.size() - level); | |
371 size_t pos = 0; | |
372 for (size_t i = level; i < uri.size(); i++, pos++) | |
373 { | |
374 trailing[pos] = uri[i]; | |
375 } | |
376 | |
377 assert(pos == trailing.size()); | |
378 | |
379 if (callback(universalHandlers_, uri, components, trailing, call)) | |
380 { | |
381 return true; | |
382 } | |
383 } | |
384 | |
385 return false; | |
386 } | |
387 | |
388 | |
389 bool GetDirectory(Json::Value& result, | |
390 const UriComponents& uri, | |
391 size_t level) | |
392 { | |
393 if (uri.size() == level) | |
394 { | |
395 if (!handlers_.HasGet() && | |
396 universalHandlers_.IsEmpty() && | |
397 wildcardChildren_.size() == 0) | |
398 { | |
399 result = Json::arrayValue; | |
400 | |
401 for (Children::const_iterator it = children_.begin(); | |
402 it != children_.end(); it++) | |
403 { | |
404 result.append(it->first); | |
405 } | |
406 | |
407 return true; | |
408 } | |
409 else | |
410 { | |
411 return false; | |
412 } | |
413 } | |
414 | |
415 Children::const_iterator child = children_.find(uri[level]); | |
416 if (child != children_.end()) | |
417 { | |
418 if (child->second->GetDirectory(result, uri, level + 1)) | |
419 { | |
420 return true; | |
421 } | |
422 } | |
423 | |
424 for (child = wildcardChildren_.begin(); | |
425 child != wildcardChildren_.end(); child++) | |
426 { | |
427 if (child->second->GetDirectory(result, uri, level + 1)) | |
428 { | |
429 return true; | |
430 } | |
431 } | |
432 | |
433 return false; | |
434 } | |
435 | |
436 | |
437 static bool GetCallback(Handlers& handlers, | |
438 const UriComponents& uri, | |
439 const RestApiPath::Components& components, | |
440 const UriComponents& trailing, | |
441 void* call) | |
442 { | |
443 for (Handlers::GetHandlers::iterator | |
444 it = handlers.getHandlers_.begin(); | |
445 it != handlers.getHandlers_.end(); it++) | |
446 { | |
447 // TODO RETURN BOOL | |
448 | |
449 (*it) (*reinterpret_cast<RestApi::GetCall*>(call)); | |
450 return true; | |
451 } | |
452 | |
453 return false; | |
454 } | |
455 | |
456 | |
457 static bool PostCallback(Handlers& handlers, | |
458 const UriComponents& uri, | |
459 const RestApiPath::Components& components, | |
460 const UriComponents& trailing, | |
461 void* call) | |
462 { | |
463 for (Handlers::PostHandlers::iterator | |
464 it = handlers.postHandlers_.begin(); | |
465 it != handlers.postHandlers_.end(); it++) | |
466 { | |
467 // TODO RETURN BOOL | |
468 | |
469 (*it) (*reinterpret_cast<RestApi::PostCall*>(call)); | |
470 return true; | |
471 } | |
472 | |
473 return false; | |
474 } | |
475 | |
476 | |
477 static bool PutCallback(Handlers& handlers, | |
478 const UriComponents& uri, | |
479 const RestApiPath::Components& components, | |
480 const UriComponents& trailing, | |
481 void* call) | |
482 { | |
483 for (Handlers::PutHandlers::iterator | |
484 it = handlers.putHandlers_.begin(); | |
485 it != handlers.putHandlers_.end(); it++) | |
486 { | |
487 // TODO RETURN BOOL | |
488 | |
489 (*it) (*reinterpret_cast<RestApi::PutCall*>(call)); | |
490 return true; | |
491 } | |
492 | |
493 return false; | |
494 } | |
495 | |
496 | |
497 static bool DeleteCallback(Handlers& handlers, | |
498 const UriComponents& uri, | |
499 const RestApiPath::Components& components, | |
500 const UriComponents& trailing, | |
501 void* call) | |
502 { | |
503 for (Handlers::DeleteHandlers::iterator | |
504 it = handlers.deleteHandlers_.begin(); | |
505 it != handlers.deleteHandlers_.end(); it++) | |
506 { | |
507 // TODO RETURN BOOL | |
508 | |
509 (*it) (*reinterpret_cast<RestApi::DeleteCall*>(call)); | |
510 return true; | |
511 } | |
512 | |
513 return false; | |
288 } | 514 } |
289 | 515 |
290 | 516 |
291 public: | 517 public: |
292 ~RestApiResource() | 518 ~RestApiHierarchy() |
293 { | 519 { |
294 DeleteChildren(children_); | 520 DeleteChildren(children_); |
295 DeleteChildren(wildcardChildren_); | 521 DeleteChildren(wildcardChildren_); |
296 } | 522 } |
297 | 523 |
298 void Register(const RestApiPath& path, | 524 void Register(const RestApiPath& path, |
299 RestApi::GetHandler handler) | 525 RestApi::GetHandler handler) |
300 { | 526 { |
301 RegisterInternal(path, handler, 0); | 527 RegisterInternal(path, handler, 0); |
302 } | 528 } |
529 | |
530 void Register(const RestApiPath& path, | |
531 RestApi::PutHandler handler) | |
532 { | |
533 RegisterInternal(path, handler, 0); | |
534 } | |
535 | |
536 void Register(const RestApiPath& path, | |
537 RestApi::PostHandler handler) | |
538 { | |
539 RegisterInternal(path, handler, 0); | |
540 } | |
541 | |
542 void Register(const RestApiPath& path, | |
543 RestApi::DeleteHandler handler) | |
544 { | |
545 RegisterInternal(path, handler, 0); | |
546 } | |
547 | |
548 void CreateSiteMap(Json::Value& target) const | |
549 { | |
550 if (children_.size() == 0) | |
551 { | |
552 std::string s = " "; | |
553 if (handlers_.getHandlers_.size() != 0) | |
554 { | |
555 s += "GET "; | |
556 } | |
557 | |
558 if (handlers_.postHandlers_.size() != 0) | |
559 { | |
560 s += "POST "; | |
561 } | |
562 | |
563 if (handlers_.putHandlers_.size() != 0) | |
564 { | |
565 s += "PUT "; | |
566 } | |
567 | |
568 if (handlers_.deleteHandlers_.size() != 0) | |
569 { | |
570 s += "DELETE "; | |
571 } | |
572 | |
573 target = s; | |
574 } | |
575 else | |
576 { | |
577 target = Json::objectValue; | |
578 | |
579 for (Children::const_iterator it = children_.begin(); | |
580 it != children_.end(); it++) | |
581 { | |
582 it->second->CreateSiteMap(target[it->first]); | |
583 } | |
584 } | |
585 | |
586 /*for (Children::const_iterator it = wildcardChildren_.begin(); | |
587 it != wildcardChildren_.end(); it++) | |
588 { | |
589 it->second->CreateSiteMap(target["* (" + it->first + ")"]); | |
590 }*/ | |
591 } | |
592 | |
593 bool GetDirectory(Json::Value& result, | |
594 const UriComponents& uri) | |
595 { | |
596 return GetDirectory(result, uri, 0); | |
597 } | |
598 | |
599 bool GetDirectory(Json::Value& result, | |
600 const std::string& uri) | |
601 { | |
602 UriComponents c; | |
603 Toolbox::SplitUriComponents(c, uri); | |
604 return GetDirectory(result, c, 0); | |
605 } | |
606 | |
607 bool Handle(RestApi::GetCall& call, | |
608 const UriComponents& uri) | |
609 { | |
610 RestApiPath::Components components; | |
611 return LookupHandler(components, uri, GetCallback, 0, &call); | |
612 } | |
613 | |
614 bool Handle(RestApi::PutCall& call, | |
615 const UriComponents& uri) | |
616 { | |
617 RestApiPath::Components components; | |
618 return LookupHandler(components, uri, PutCallback, 0, &call); | |
619 } | |
620 | |
621 bool Handle(RestApi::PostCall& call, | |
622 const UriComponents& uri) | |
623 { | |
624 RestApiPath::Components components; | |
625 return LookupHandler(components, uri, PostCallback, 0, &call); | |
626 } | |
627 | |
628 bool Handle(RestApi::DeleteCall& call, | |
629 const UriComponents& uri) | |
630 { | |
631 RestApiPath::Components components; | |
632 return LookupHandler(components, uri, DeleteCallback, 0, &call); | |
633 } | |
634 | |
635 bool Handle(RestApi::GetCall& call, | |
636 const std::string& uri) | |
637 { | |
638 UriComponents c; | |
639 Toolbox::SplitUriComponents(c, uri); | |
640 return Handle(call, c); | |
641 } | |
303 }; | 642 }; |
304 | 643 |
305 } | 644 } |
306 | 645 |
307 | 646 |
308 | 647 |
309 static void Toto(RestApi::GetCall& get) | 648 |
649 static int testValue; | |
650 | |
651 template <int value> | |
652 static void SetValue(RestApi::GetCall& get) | |
310 { | 653 { |
654 testValue = value; | |
311 } | 655 } |
312 | 656 |
313 | 657 |
314 TEST(RestApi, RestApiResource) | 658 TEST(RestApi, RestApiHierarchy) |
315 { | 659 { |
316 RestApiResource root; | 660 RestApiHierarchy root; |
317 | 661 root.Register(RestApiPath("/hello/world/test"), SetValue<1>); |
318 root.Register(RestApiPath("/hello/world/test"), Toto); | 662 root.Register(RestApiPath("/hello/world/test2"), SetValue<2>); |
663 root.Register(RestApiPath("/hello/{world}/test3/test4"), SetValue<3>); | |
664 root.Register(RestApiPath("/hello2/*"), SetValue<4>); | |
665 | |
666 Json::Value m; | |
667 root.CreateSiteMap(m); | |
668 std::cout << m; | |
669 | |
670 Json::Value d; | |
671 ASSERT_FALSE(root.GetDirectory(d, "/hello")); | |
672 | |
673 ASSERT_TRUE(root.GetDirectory(d, "/hello/a")); | |
674 ASSERT_EQ(1u, d.size()); | |
675 ASSERT_EQ("test3", d[0].asString()); | |
676 | |
677 ASSERT_TRUE(root.GetDirectory(d, "/hello/world")); | |
678 ASSERT_EQ(2u, d.size()); | |
679 | |
680 ASSERT_TRUE(root.GetDirectory(d, "/hello/a/test3")); | |
681 ASSERT_EQ(1u, d.size()); | |
682 ASSERT_EQ("test4", d[0].asString()); | |
683 | |
684 ASSERT_FALSE(root.GetDirectory(d, "/hello/world/test")); | |
685 ASSERT_FALSE(root.GetDirectory(d, "/hello/world/test2")); | |
686 ASSERT_FALSE(root.GetDirectory(d, "/hello2")); | |
687 | |
688 testValue = 0; | |
689 ASSERT_TRUE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello/world/test")); | |
690 ASSERT_EQ(testValue, 1); | |
691 ASSERT_TRUE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello/world/test2")); | |
692 ASSERT_EQ(testValue, 2); | |
693 ASSERT_TRUE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello/b/test3/test4")); | |
694 ASSERT_EQ(testValue, 3); | |
695 ASSERT_FALSE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello/b/test3/test")); | |
696 ASSERT_EQ(testValue, 3); | |
697 ASSERT_TRUE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello2/a/b")); | |
698 ASSERT_EQ(testValue, 4); | |
319 } | 699 } |