comparison OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp @ 1569:27774f6f84e4

improved error handling in http answers
author jodogne
date Sun, 23 Aug 2015 11:01:15 +0200
parents 3be6eb3757c8
children 2bd2c280f9b5
comparison
equal deleted inserted replaced
1568:818ae9bc493a 1569:27774f6f84e4
312 **/ 312 **/
313 313
314 std::string modifiedInstance; 314 std::string modifiedInstance;
315 if (context.Store(modifiedInstance, toStore) != StoreStatus_Success) 315 if (context.Store(modifiedInstance, toStore) != StoreStatus_Success)
316 { 316 {
317 LOG(ERROR) << "Error while storing a modified instance " << *it; 317 throw OrthancException("Error while storing a modified instance " + *it);
318 return;
319 } 318 }
320 319
321 // Sanity checks in debug mode 320 // Sanity checks in debug mode
322 assert(modifiedInstance == modifiedHasher.HashInstance()); 321 assert(modifiedInstance == modifiedHasher.HashInstance());
323 322
429 changeType, resourceType, call); 428 changeType, resourceType, call);
430 } 429 }
431 } 430 }
432 431
433 432
434 static bool StoreCreatedInstance(std::string& id /* out */, 433 static void StoreCreatedInstance(std::string& id /* out */,
435 ServerContext& context, 434 ServerContext& context,
436 ParsedDicomFile& dicom) 435 ParsedDicomFile& dicom)
437 { 436 {
438 DicomInstanceToStore toStore; 437 DicomInstanceToStore toStore;
439 toStore.SetParsedDicomFile(dicom); 438 toStore.SetParsedDicomFile(dicom);
440 439
441 StoreStatus status = context.Store(id, toStore); 440 StoreStatus status = context.Store(id, toStore);
442 441
443 if (status == StoreStatus_Failure) 442 if (status == StoreStatus_Failure)
444 { 443 {
445 LOG(ERROR) << "Error while storing a manually-created instance"; 444 throw OrthancException("Error while storing a manually-created instance");
446 return false; 445 }
447 } 446 }
448 else 447
449 { 448
450 return true; 449 static void CreateDicomV1(ParsedDicomFile& dicom,
451 }
452 }
453
454
455 static bool CreateDicomV1(ParsedDicomFile& dicom,
456 const Json::Value& request) 450 const Json::Value& request)
457 { 451 {
458 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}' 452 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}'
459 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}' 453 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}'
460 454
465 for (size_t i = 0; i < members.size(); i++) 459 for (size_t i = 0; i < members.size(); i++)
466 { 460 {
467 const std::string& name = members[i]; 461 const std::string& name = members[i];
468 if (request[name].type() != Json::stringValue) 462 if (request[name].type() != Json::stringValue)
469 { 463 {
470 LOG(ERROR) << "Only string values are supported when creating DICOM instances"; 464 throw OrthancException("Only string values are supported when creating DICOM instances");
471 return false;
472 } 465 }
473 466
474 std::string value = request[name].asString(); 467 std::string value = request[name].asString();
475 468
476 DicomTag tag = FromDcmtkBridge::ParseTag(name); 469 DicomTag tag = FromDcmtkBridge::ParseTag(name);
481 else 474 else
482 { 475 {
483 dicom.Replace(tag, value); 476 dicom.Replace(tag, value);
484 } 477 }
485 } 478 }
486 479 }
487 return true; 480
488 } 481
489 482 static void InjectTags(ParsedDicomFile& dicom,
490
491 static bool InjectTags(ParsedDicomFile& dicom,
492 const Json::Value& tags) 483 const Json::Value& tags)
493 { 484 {
494 if (tags.type() != Json::objectValue) 485 if (tags.type() != Json::objectValue)
495 { 486 {
496 LOG(ERROR) << "Bad syntax to specify the tags"; 487 throw OrthancException("Bad syntax to specify the tags");
497 return false;
498 } 488 }
499 489
500 // Inject the user-specified tags 490 // Inject the user-specified tags
501 Json::Value::Members members = tags.getMemberNames(); 491 Json::Value::Members members = tags.getMemberNames();
502 for (size_t i = 0; i < members.size(); i++) 492 for (size_t i = 0; i < members.size(); i++)
503 { 493 {
504 const std::string& name = members[i]; 494 const std::string& name = members[i];
505 if (tags[name].type() != Json::stringValue) 495 if (tags[name].type() != Json::stringValue)
506 { 496 {
507 LOG(ERROR) << "Only string values are supported when creating DICOM instances"; 497 throw OrthancException("Only string values are supported when creating DICOM instances");
508 return false;
509 } 498 }
510 499
511 std::string value = tags[name].asString(); 500 std::string value = tags[name].asString();
512 501
513 DicomTag tag = FromDcmtkBridge::ParseTag(name); 502 DicomTag tag = FromDcmtkBridge::ParseTag(name);
514 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET) 503 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET)
515 { 504 {
516 if (tag != DICOM_TAG_PATIENT_ID && 505 if (tag != DICOM_TAG_PATIENT_ID &&
517 dicom.HasTag(tag)) 506 dicom.HasTag(tag))
518 { 507 {
519 LOG(ERROR) << "Trying to override a value inherited from a parent module"; 508 throw OrthancException("Trying to override a value inherited from a parent module");
520 return false;
521 } 509 }
522 510
523 if (tag == DICOM_TAG_PIXEL_DATA) 511 if (tag == DICOM_TAG_PIXEL_DATA)
524 { 512 {
525 LOG(ERROR) << "Use \"Content\" to inject an image into a new DICOM instance"; 513 throw OrthancException("Use \"Content\" to inject an image into a new DICOM instance");
526 return false;
527 } 514 }
528 else 515 else
529 { 516 {
530 dicom.Replace(tag, Toolbox::ConvertFromUtf8(value, dicom.GetEncoding())); 517 dicom.Replace(tag, Toolbox::ConvertFromUtf8(value, dicom.GetEncoding()));
531 } 518 }
532 } 519 }
533 } 520 }
534
535 return true;
536 } 521 }
537 522
538 523
539 static void CreateSeries(RestApiPostCall& call, 524 static void CreateSeries(RestApiPostCall& call,
540 ParsedDicomFile& base /* in */, 525 ParsedDicomFile& base /* in */,
547 base.Replace(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size())); 532 base.Replace(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size()));
548 base.Replace(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1"); 533 base.Replace(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1");
549 534
550 std::string someInstance; 535 std::string someInstance;
551 536
552 bool ok = true;
553
554 try 537 try
555 { 538 {
556 for (Json::ArrayIndex i = 0; ok && i < content.size(); i++) 539 for (Json::ArrayIndex i = 0; i < content.size(); i++)
557 { 540 {
558 std::auto_ptr<ParsedDicomFile> dicom(base.Clone()); 541 std::auto_ptr<ParsedDicomFile> dicom(base.Clone());
559 const Json::Value* payload = NULL; 542 const Json::Value* payload = NULL;
560 543
561 if (content[i].type() == Json::stringValue) 544 if (content[i].type() == Json::stringValue)
564 } 547 }
565 else if (content[i].type() == Json::objectValue) 548 else if (content[i].type() == Json::objectValue)
566 { 549 {
567 if (!content[i].isMember("Content")) 550 if (!content[i].isMember("Content"))
568 { 551 {
569 LOG(ERROR) << "No payload is present for one instance in the series"; 552 throw OrthancException("No payload is present for one instance in the series");
570 ok = false;
571 break;
572 } 553 }
573 554
574 payload = &content[i]["Content"]; 555 payload = &content[i]["Content"];
575 556
576 if (content[i].isMember("Tags") && 557 if (content[i].isMember("Tags"))
577 !InjectTags(*dicom, content[i]["Tags"])) 558 {
578 { 559 InjectTags(*dicom, content[i]["Tags"]);
579 ok = false; 560 }
580 break;
581 }
582 } 561 }
583 562
584 if (payload == NULL || 563 if (payload == NULL ||
585 payload->type() != Json::stringValue) 564 payload->type() != Json::stringValue)
586 { 565 {
587 LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme"; 566 throw OrthancException("The payload of the DICOM instance must be specified according to Data URI scheme");
588 ok = false;
589 break;
590 } 567 }
591 568
592 dicom->EmbedContent(payload->asString()); 569 dicom->EmbedContent(payload->asString());
593 dicom->Replace(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1)); 570 dicom->Replace(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1));
594 dicom->Replace(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1)); 571 dicom->Replace(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1));
595 572
596 if (!StoreCreatedInstance(someInstance, context, *dicom)) 573 StoreCreatedInstance(someInstance, context, *dicom);
597 { 574 }
598 LOG(ERROR) << "Error while creating the series"; 575 }
599 ok = false; 576 catch (OrthancException& e)
600 break; 577 {
601 } 578 // Error: Remove the newly-created series
602 } 579
603 } 580 std::string series;
604 catch (OrthancException&) 581 if (context.GetIndex().LookupParent(series, someInstance))
605 { 582 {
606 ok = false; 583 Json::Value dummy;
584 context.GetIndex().DeleteResource(dummy, series, ResourceType_Series);
585 }
586
587 throw e;
607 } 588 }
608 589
609 std::string series; 590 std::string series;
610 if (context.GetIndex().LookupParent(series, someInstance)) 591 if (context.GetIndex().LookupParent(series, someInstance))
611 { 592 {
612 if (ok) 593 OrthancRestApi::GetApi(call).AnswerStoredResource(call, series, ResourceType_Series, StoreStatus_Success);
613 {
614 OrthancRestApi::GetApi(call).AnswerStoredResource(call, series, ResourceType_Series, StoreStatus_Success);
615 }
616 else
617 {
618 // Error: Remove the newly-created series
619 Json::Value dummy;
620 context.GetIndex().DeleteResource(dummy, series, ResourceType_Series);
621 }
622 } 594 }
623 } 595 }
624 596
625 597
626 static void CreateDicomV2(RestApiPostCall& call, 598 static void CreateDicomV2(RestApiPostCall& call,
630 ServerContext& context = OrthancRestApi::GetContext(call); 602 ServerContext& context = OrthancRestApi::GetContext(call);
631 603
632 if (!request.isMember("Tags") || 604 if (!request.isMember("Tags") ||
633 request["Tags"].type() != Json::objectValue) 605 request["Tags"].type() != Json::objectValue)
634 { 606 {
635 return; 607 throw OrthancException(ErrorCode_BadRequest);
636 } 608 }
637 609
638 ParsedDicomFile dicom; 610 ParsedDicomFile dicom;
639 611
640 { 612 {
643 if (request["Tags"].isMember("SpecificCharacterSet")) 615 if (request["Tags"].isMember("SpecificCharacterSet"))
644 { 616 {
645 const char* tmp = request["Tags"]["SpecificCharacterSet"].asCString(); 617 const char* tmp = request["Tags"]["SpecificCharacterSet"].asCString();
646 if (!GetDicomEncoding(encoding, tmp)) 618 if (!GetDicomEncoding(encoding, tmp))
647 { 619 {
648 LOG(ERROR) << "Unknown specific character set: " << tmp; 620 throw OrthancException("Unknown specific character set: " + std::string(tmp));
649 return;
650 } 621 }
651 } 622 }
652 else 623 else
653 { 624 {
654 std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1"); 625 std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1");
664 { 635 {
665 // Locate the parent tags 636 // Locate the parent tags
666 std::string parent = request["Parent"].asString(); 637 std::string parent = request["Parent"].asString();
667 if (!context.GetIndex().LookupResourceType(parentType, parent)) 638 if (!context.GetIndex().LookupResourceType(parentType, parent))
668 { 639 {
669 LOG(ERROR) << "Trying to attach a new DICOM instance to an inexistent resource: " << parent; 640 throw OrthancException("Trying to attach a new DICOM instance to an inexistent resource: " + parent);
670 return;
671 } 641 }
672 642
673 if (parentType == ResourceType_Instance) 643 if (parentType == ResourceType_Instance)
674 { 644 {
675 LOG(ERROR) << "Trying to attach a new DICOM instance to an instance (must be a series, study or patient): " << parent; 645 throw OrthancException("Trying to attach a new DICOM instance to an instance (must be a series, study or patient): " + parent);
676 return;
677 } 646 }
678 647
679 // Select one existing child instance of the parent resource, to 648 // Select one existing child instance of the parent resource, to
680 // retrieve all its tags 649 // retrieve all its tags
681 Json::Value siblingTags; 650 Json::Value siblingTags;
684 // Retrieve all the instances of the parent resource 653 // Retrieve all the instances of the parent resource
685 std::list<std::string> siblingInstances; 654 std::list<std::string> siblingInstances;
686 context.GetIndex().GetChildInstances(siblingInstances, parent); 655 context.GetIndex().GetChildInstances(siblingInstances, parent);
687 656
688 if (siblingInstances.empty()) 657 if (siblingInstances.empty())
689 { 658 {
690 return; // Error: No instance (should never happen) 659 // Error: No instance (should never happen)
660 throw OrthancException(ErrorCode_InternalError);
691 } 661 }
692 662
693 context.ReadJson(siblingTags, siblingInstances.front()); 663 context.ReadJson(siblingTags, siblingInstances.front());
694 } 664 }
695 665
703 Encoding encoding; 673 Encoding encoding;
704 if (!siblingTags[SPECIFIC_CHARACTER_SET].isMember("Value") || 674 if (!siblingTags[SPECIFIC_CHARACTER_SET].isMember("Value") ||
705 siblingTags[SPECIFIC_CHARACTER_SET]["Value"].type() != Json::stringValue || 675 siblingTags[SPECIFIC_CHARACTER_SET]["Value"].type() != Json::stringValue ||
706 !GetDicomEncoding(encoding, siblingTags[SPECIFIC_CHARACTER_SET]["Value"].asCString())) 676 !GetDicomEncoding(encoding, siblingTags[SPECIFIC_CHARACTER_SET]["Value"].asCString()))
707 { 677 {
708 LOG(ERROR) << "Unable to get the encoding of the parent resource"; 678 throw OrthancException("Unable to get the encoding of the parent resource");
709 return;
710 } 679 }
711 680
712 dicom.SetEncoding(encoding); 681 dicom.SetEncoding(encoding);
713 } 682 }
714 } 683 }
730 699
731 // Go up 700 // Go up
732 std::string tmp; 701 std::string tmp;
733 if (!context.GetIndex().LookupParent(tmp, parent)) 702 if (!context.GetIndex().LookupParent(tmp, parent))
734 { 703 {
735 return; 704 throw OrthancException(ErrorCode_InternalError);
736 } 705 }
737 706
738 parent = tmp; 707 parent = tmp;
739 type = GetParentResourceType(type); 708 type = GetParentResourceType(type);
740 } 709 }
784 dicom.Replace(DICOM_TAG_STUDY_DATE, date); 753 dicom.Replace(DICOM_TAG_STUDY_DATE, date);
785 dicom.Replace(DICOM_TAG_STUDY_TIME, time); 754 dicom.Replace(DICOM_TAG_STUDY_TIME, time);
786 } 755 }
787 756
788 757
789 if (!InjectTags(dicom, request["Tags"])) 758 InjectTags(dicom, request["Tags"]);
790 {
791 return; // Error
792 }
793 759
794 760
795 // Inject the content (either an image, or a PDF file) 761 // Inject the content (either an image, or a PDF file)
796 if (request.isMember("Content")) 762 if (request.isMember("Content"))
797 { 763 {
811 return; 777 return;
812 } 778 }
813 } 779 }
814 else 780 else
815 { 781 {
816 LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme"; 782 throw OrthancException("The payload of the DICOM instance must be specified according to Data URI scheme");
817 return; 783 return;
818 } 784 }
819 } 785 }
820 786
821 std::string id; 787 std::string id;
822 if (StoreCreatedInstance(id, context, dicom)) 788 StoreCreatedInstance(id, context, dicom);
823 { 789 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
824 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
825 }
826 790
827 return; 791 return;
828 } 792 }
829 793
830 794
831 static void CreateDicom(RestApiPostCall& call) 795 static void CreateDicom(RestApiPostCall& call)
832 { 796 {
833 Json::Value request; 797 Json::Value request;
834 if (call.ParseJsonRequest(request) && 798 if (!call.ParseJsonRequest(request) ||
835 request.isObject()) 799 !request.isObject())
836 { 800 {
837 if (request.isMember("Tags")) 801 throw OrthancException(ErrorCode_BadRequest);
838 { 802 }
839 CreateDicomV2(call, request); 803
840 } 804 if (request.isMember("Tags"))
841 else 805 {
842 { 806 CreateDicomV2(call, request);
843 // Compatibility with Orthanc <= 0.9.3 807 }
844 ServerContext& context = OrthancRestApi::GetContext(call); 808 else
845 ParsedDicomFile dicom; 809 {
846 if (CreateDicomV1(dicom, request)) 810 // Compatibility with Orthanc <= 0.9.3
847 { 811 ServerContext& context = OrthancRestApi::GetContext(call);
848 std::string id; 812 ParsedDicomFile dicom;
849 if (StoreCreatedInstance(id, context, dicom)) 813 CreateDicomV1(dicom, request);
850 { 814
851 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success); 815 std::string id;
852 } 816 StoreCreatedInstance(id, context, dicom);
853 } 817 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
854 } 818 }
855 }
856 } 819 }
857 820
858 821
859 void OrthancRestApi::RegisterAnonymizeModify() 822 void OrthancRestApi::RegisterAnonymizeModify()
860 { 823 {