comparison UnitTestsSources/RestApiTests.cpp @ 969:3dce528b0cc2

RestApiHierarchy
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 30 Jun 2014 13:17:49 +0200
parents 8ed284e79850
children 1a3817d84f39
comparison
equal deleted inserted replaced
968:8ed284e79850 969:3dce528b0cc2
40 #include "../Core/HttpClient.h" 40 #include "../Core/HttpClient.h"
41 #include "../Core/RestApi/RestApi.h" 41 #include "../Core/RestApi/RestApi.h"
42 #include "../Core/Uuid.h" 42 #include "../Core/Uuid.h"
43 #include "../Core/OrthancException.h" 43 #include "../Core/OrthancException.h"
44 #include "../Core/Compression/ZlibCompressor.h" 44 #include "../Core/Compression/ZlibCompressor.h"
45 #include "../Core/RestApi/RestApiHierarchy.h"
45 46
46 using namespace Orthanc; 47 using namespace Orthanc;
47 48
48 #if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS) 49 #if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS)
49 #error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS" 50 #error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS"
190 191
191 192
192 193
193 194
194 195
195 namespace Orthanc
196 {
197 class RestApiHierarchy
198 {
199 private:
200 struct Handlers
201 {
202 typedef std::list<RestApi::GetHandler> GetHandlers;
203 typedef std::list<RestApi::PostHandler> PostHandlers;
204 typedef std::list<RestApi::PutHandler> PutHandlers;
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 }
216
217 void Register(RestApi::GetHandler handler)
218 {
219 getHandlers_.push_back(handler);
220 }
221
222 void Register(RestApi::PutHandler handler)
223 {
224 putHandlers_.push_back(handler);
225 }
226
227 void Register(RestApi::PostHandler handler)
228 {
229 postHandlers_.push_back(handler);
230 }
231
232 void Register(RestApi::DeleteHandler handler)
233 {
234 deleteHandlers_.push_back(handler);
235 }
236
237 bool IsEmpty() const
238 {
239 return (getHandlers_.empty() &&
240 postHandlers_.empty() &&
241 putHandlers_.empty() &&
242 deleteHandlers_.empty());
243 }
244 };
245
246
247 typedef std::map<std::string, RestApiHierarchy*> Children;
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_;
255 Children children_;
256 Children wildcardChildren_;
257 Handlers universalHandlers_;
258
259
260 static RestApiHierarchy& AddChild(Children& children,
261 const std::string& name)
262 {
263 Children::iterator it = children.find(name);
264
265 if (it == children.end())
266 {
267 // Create new child
268 RestApiHierarchy *child = new RestApiHierarchy;
269 children[name] = child;
270 return *child;
271 }
272 else
273 {
274 return *it->second;
275 }
276 }
277
278
279 static void DeleteChildren(Children& children)
280 {
281 for (Children::iterator it = children.begin();
282 it != children.end(); it++)
283 {
284 delete it->second;
285 }
286 }
287
288
289 template <typename Handler>
290 void RegisterInternal(const RestApiPath& path,
291 Handler handler,
292 size_t level)
293 {
294 if (path.GetLevelCount() == level)
295 {
296 if (path.IsUniversalTrailing())
297 {
298 universalHandlers_.Register(handler);
299 }
300 else
301 {
302 handlers_.Register(handler);
303 }
304 }
305 else
306 {
307 RestApiHierarchy* child;
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;
514 }
515
516
517 public:
518 ~RestApiHierarchy()
519 {
520 DeleteChildren(children_);
521 DeleteChildren(wildcardChildren_);
522 }
523
524 void Register(const RestApiPath& path,
525 RestApi::GetHandler handler)
526 {
527 RegisterInternal(path, handler, 0);
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 }
642 };
643
644 }
645
646
647
648
649 static int testValue; 196 static int testValue;
650 197
651 template <int value> 198 template <int value>
652 static void SetValue(RestApi::GetCall& get) 199 static void SetValue(RestApi::GetCall& get)
653 { 200 {
654 testValue = value; 201 testValue = value;
202 }
203
204
205 static bool GetDirectory(Json::Value& target,
206 RestApiHierarchy& hierarchy,
207 const std::string& uri)
208 {
209 UriComponents p;
210 Toolbox::SplitUriComponents(p, uri);
211 return hierarchy.GetDirectory(target, p);
212 }
213
214
215 static bool HandleGet(RestApiHierarchy& hierarchy,
216 const std::string& uri)
217 {
218 UriComponents p;
219 Toolbox::SplitUriComponents(p, uri);
220 return hierarchy.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), p);
655 } 221 }
656 222
657 223
658 TEST(RestApi, RestApiHierarchy) 224 TEST(RestApi, RestApiHierarchy)
659 { 225 {
666 Json::Value m; 232 Json::Value m;
667 root.CreateSiteMap(m); 233 root.CreateSiteMap(m);
668 std::cout << m; 234 std::cout << m;
669 235
670 Json::Value d; 236 Json::Value d;
671 ASSERT_FALSE(root.GetDirectory(d, "/hello")); 237 ASSERT_FALSE(GetDirectory(d, root, "/hello"));
672 238
673 ASSERT_TRUE(root.GetDirectory(d, "/hello/a")); 239 ASSERT_TRUE(GetDirectory(d, root, "/hello/a"));
674 ASSERT_EQ(1u, d.size()); 240 ASSERT_EQ(1u, d.size());
675 ASSERT_EQ("test3", d[0].asString()); 241 ASSERT_EQ("test3", d[0].asString());
676 242
677 ASSERT_TRUE(root.GetDirectory(d, "/hello/world")); 243 ASSERT_TRUE(GetDirectory(d, root, "/hello/world"));
678 ASSERT_EQ(2u, d.size()); 244 ASSERT_EQ(2u, d.size());
679 245
680 ASSERT_TRUE(root.GetDirectory(d, "/hello/a/test3")); 246 ASSERT_TRUE(GetDirectory(d, root, "/hello/a/test3"));
681 ASSERT_EQ(1u, d.size()); 247 ASSERT_EQ(1u, d.size());
682 ASSERT_EQ("test4", d[0].asString()); 248 ASSERT_EQ("test4", d[0].asString());
683 249
684 ASSERT_FALSE(root.GetDirectory(d, "/hello/world/test")); 250 ASSERT_FALSE(GetDirectory(d, root, "/hello/world/test"));
685 ASSERT_FALSE(root.GetDirectory(d, "/hello/world/test2")); 251 ASSERT_FALSE(GetDirectory(d, root, "/hello/world/test2"));
686 ASSERT_FALSE(root.GetDirectory(d, "/hello2")); 252 ASSERT_FALSE(GetDirectory(d, root, "/hello2"));
687 253
688 testValue = 0; 254 testValue = 0;
689 ASSERT_TRUE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello/world/test")); 255 ASSERT_TRUE(HandleGet(root, "/hello/world/test"));
690 ASSERT_EQ(testValue, 1); 256 ASSERT_EQ(testValue, 1);
691 ASSERT_TRUE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello/world/test2")); 257 ASSERT_TRUE(HandleGet(root, "/hello/world/test2"));
692 ASSERT_EQ(testValue, 2); 258 ASSERT_EQ(testValue, 2);
693 ASSERT_TRUE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello/b/test3/test4")); 259 ASSERT_TRUE(HandleGet(root, "/hello/b/test3/test4"));
694 ASSERT_EQ(testValue, 3); 260 ASSERT_EQ(testValue, 3);
695 ASSERT_FALSE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello/b/test3/test")); 261 ASSERT_FALSE(HandleGet(root, "/hello/b/test3/test"));
696 ASSERT_EQ(testValue, 3); 262 ASSERT_EQ(testValue, 3);
697 ASSERT_TRUE(root.Handle(*reinterpret_cast<RestApi::GetCall*>(NULL), "/hello2/a/b")); 263 ASSERT_TRUE(HandleGet(root, "/hello2/a/b"));
698 ASSERT_EQ(testValue, 4); 264 ASSERT_EQ(testValue, 4);
699 } 265 }