Mercurial > hg > orthanc
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 } |