changeset 4416:0b27841950d5

openapi about creation of zip/media archives
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 28 Dec 2020 16:49:17 +0100
parents b50410d0e98c
children a4518adede59
files OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp
diffstat 2 files changed, 139 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp	Mon Dec 28 15:32:01 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp	Mon Dec 28 16:49:17 2020 +0100
@@ -194,11 +194,56 @@
     }
   }
 
+
+  static void DocumentPostArguments(RestApiPostCall& call,
+                                    bool isMedia,
+                                    bool defaultExtended)
+  {
+    call.GetDocumentation()
+      .SetRequestField("Synchronous", RestApiCallDocumentation::Type_Boolean,
+                     "If `true`, create the archive in synchronous mode, which means that the HTTP answer will directly "
+                     "contain the ZIP file. This is the default, easy behavior, but it is *not* be desirable to archive "
+                     "large amount of data, as it might lead to network timeouts.", false)
+      .SetRequestField("Asynchronous", RestApiCallDocumentation::Type_Boolean,
+                       "If `true`, create the archive in asynchronous mode, which means that a job is submitted to create "
+                       "the archive in background. Prefer this flavor wherever possible.", false)
+      .SetRequestField(KEY_TRANSCODE, RestApiCallDocumentation::Type_String,
+                       "If present, the DICOM files in the archive will be transcoded to the provided "
+                       "transfer syntax: https://book.orthanc-server.com/faq/transcoding.html", false)
+      .SetRequestField("Priority", RestApiCallDocumentation::Type_Number,
+                       "In asynchronous mode, the priority of the job. The lower the value, the higher the priority.", false)
+      .AddAnswerType(MimeType_Zip, "In synchronous mode, the ZIP file containing the archive")
+      .AddAnswerType(MimeType_Json, "In asynchronous mode, information about the job that has been submitted to "
+                     "generate the archive: https://book.orthanc-server.com/users/advanced-rest.html#jobs");
+
+    if (isMedia)
+    {
+      call.GetDocumentation().SetRequestField(
+        KEY_EXTENDED, RestApiCallDocumentation::Type_Boolean, "If `true`, will include additional "
+        "tags such as `SeriesDescription`, leading to a so-called *extended DICOMDIR*. Default value is " +
+        std::string(defaultExtended ? "`true`" : "`false`") + ".", false);
+    }
+  }
+
   
   template <bool IS_MEDIA,
             bool DEFAULT_IS_EXTENDED  /* only makes sense for media (i.e. not ZIP archives) */ >
   static void CreateBatch(RestApiPostCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      DocumentPostArguments(call, IS_MEDIA, DEFAULT_IS_EXTENDED);
+      std::string m = (IS_MEDIA ? "DICOMDIR media" : "ZIP archive");
+      call.GetDocumentation()
+        .SetTag("System")
+        .SetSummary("Create " + m)
+        .SetDescription("Create a " + m + " containing the DICOM resources (patients, studies, series, or instances) "
+                        "whose Orthanc identifiers are provided in the body")
+        .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings,
+                         "The list of Orthanc identifiers of interest.", false);
+      return;
+    }
+
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     Json::Value body;
@@ -228,10 +273,35 @@
   }
   
 
-  template <bool IS_MEDIA,
-            bool DEFAULT_IS_EXTENDED  /* only makes sense for media (i.e. not ZIP archives) */ >
+  template <bool IS_MEDIA>
   static void CreateSingleGet(RestApiGetCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str());
+      std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */);
+      std::string m = (IS_MEDIA ? "DICOMDIR media" : "ZIP archive");
+      call.GetDocumentation()
+        .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */))
+        .SetSummary("Create " + m)
+        .SetDescription("Synchronously create a " + m + " containing the DICOM " + r +
+                        " whose Orthanc identifier is provided in the URL. This flavor is synchronous, "
+                        "which might *not* be desirable to archive large amount of data, as it might "
+                        "lead to network timeouts. Prefer the asynchronous version using `POST` method.")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
+        .SetHttpGetArgument("transcode", RestApiCallDocumentation::Type_String,
+                            "If present, the DICOM files in the archive will be transcoded to the provided "
+                            "transfer syntax: https://book.orthanc-server.com/faq/transcoding.html", false)
+        .AddAnswerType(MimeType_Zip, "ZIP file containing the archive");
+      if (IS_MEDIA)
+      {
+        call.GetDocumentation().SetHttpGetArgument(
+          "extended", RestApiCallDocumentation::Type_String,
+          "If present, will include additional tags such as `SeriesDescription`, leading to a so-called *extended DICOMDIR*", false);
+      }
+      return;
+    }
+
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     std::string id = call.GetUriComponent("id", "");
@@ -260,10 +330,24 @@
   }
 
 
-  template <bool IS_MEDIA,
-            bool DEFAULT_IS_EXTENDED  /* only makes sense for media (i.e. not ZIP archives) */ >
+  template <bool IS_MEDIA>
   static void CreateSinglePost(RestApiPostCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      DocumentPostArguments(call, IS_MEDIA, false /* not extended by default */);
+      ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str());
+      std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */);
+      std::string m = (IS_MEDIA ? "DICOMDIR media" : "ZIP archive");
+      call.GetDocumentation()
+        .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */))
+        .SetSummary("Create " + m)
+        .SetDescription("Create a " + m + " containing the DICOM " + r +
+                        " whose Orthanc identifier is provided in the URL")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest");
+      return;
+    }
+
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     std::string id = call.GetUriComponent("id", "");
@@ -275,7 +359,7 @@
       DicomTransferSyntax transferSyntax;
       int priority;
       GetJobParameters(synchronous, extended, transcode, transferSyntax,
-                       priority, body, DEFAULT_IS_EXTENDED);
+                       priority, body, false /* by default, not extented */);
       
       std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended));
       job->AddResource(id);
@@ -296,33 +380,18 @@
     
   void OrthancRestApi::RegisterArchive()
   {
-    Register("/patients/{id}/archive",
-             CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>);
-    Register("/studies/{id}/archive",
-             CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>);
-    Register("/series/{id}/archive",
-             CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>);
-
-    Register("/patients/{id}/archive",
-             CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>);
-    Register("/studies/{id}/archive",
-             CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>);
-    Register("/series/{id}/archive",
-             CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>);
-
-    Register("/patients/{id}/media",
-             CreateSingleGet<true /* media */, false /* not extended by default */>);
-    Register("/studies/{id}/media",
-             CreateSingleGet<true /* media */, false /* not extended by default */>);
-    Register("/series/{id}/media",
-             CreateSingleGet<true /* media */, false /* not extended by default */>);
-
-    Register("/patients/{id}/media",
-             CreateSinglePost<true /* media */, false /* not extended by default */>);
-    Register("/studies/{id}/media",
-             CreateSinglePost<true /* media */, false /* not extended by default */>);
-    Register("/series/{id}/media",
-             CreateSinglePost<true /* media */, false /* not extended by default */>);
+    Register("/patients/{id}/archive", CreateSingleGet<false /* ZIP */>);
+    Register("/patients/{id}/archive", CreateSinglePost<false /* ZIP */>);
+    Register("/patients/{id}/media",   CreateSingleGet<true /* media */>);
+    Register("/patients/{id}/media",   CreateSinglePost<true /* media */>);
+    Register("/series/{id}/archive",   CreateSingleGet<false /* ZIP */>);
+    Register("/series/{id}/archive",   CreateSinglePost<false /* ZIP */>);
+    Register("/series/{id}/media",     CreateSingleGet<true /* media */>);
+    Register("/series/{id}/media",     CreateSinglePost<true /* media */>);
+    Register("/studies/{id}/archive",  CreateSingleGet<false /* ZIP */>);
+    Register("/studies/{id}/archive",  CreateSinglePost<false /* ZIP */>);
+    Register("/studies/{id}/media",    CreateSingleGet<true /* media */>);
+    Register("/studies/{id}/media",    CreateSinglePost<true /* media */>);
 
     Register("/tools/create-archive",
              CreateBatch<false /* ZIP */,  false /* extended makes no sense in ZIP */>);
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Mon Dec 28 15:32:01 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Mon Dec 28 16:49:17 2020 +0100
@@ -249,7 +249,7 @@
       call.GetDocumentation()
         .SetTag(GetResourceTypeText(resourceType, true /* plural */, true /* upper case */))
         .SetSummary("Get information about some " + resource)
-        .SetDescription("Get detailed information about the DICOM " + resource + " of interest whose Orthanc identifier is provided in the URL")
+        .SetDescription("Get detailed information about the DICOM " + resource + " whose Orthanc identifier is provided in the URL")
         .SetUriArgument("id", "Orthanc identifier of the " + resource + " of interest")
         .AddAnswerType(MimeType_Json, "Information about the DICOM " + resource)
         .SetHttpGetSample(GetDocumentationSampleResource(resourceType), true);
@@ -272,7 +272,7 @@
       call.GetDocumentation()
         .SetTag(GetResourceTypeText(resourceType, true /* plural */, true /* upper case */))
         .SetSummary("Delete some " + resource)
-        .SetDescription("Delete the DICOM " + resource + " of interest whose Orthanc identifier is provided in the URL")
+        .SetDescription("Delete the DICOM " + resource + " whose Orthanc identifier is provided in the URL")
         .SetUriArgument("id", "Orthanc identifier of the " + resource + " of interest");
       return;
     }
@@ -435,6 +435,8 @@
           .SetSummary("Get human-readable tags")
           .SetDescription("Get the DICOM tags in human-readable format")
           .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest")
+          .SetHttpGetArgument("ignore-length", RestApiCallDocumentation::Type_JsonListOfStrings,
+                              "Also include the DICOM tags that are provided in this list, even if their associated value is long", false)
           .AddAnswerType(MimeType_Json, "JSON object containing the DICOM tags and their associated value")
           .SetTruncatedJsonHttpGetSample("https://demo.orthanc-server.com/instances/7c92ce8e-bbf67ed2-ffa3b8c1-a3b35d94-7ff3ae26/simplified-tags", 10);
         return;
@@ -2077,6 +2079,21 @@
 
   static void GetSharedTags(RestApiGetCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str());
+      std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */);
+      call.GetDocumentation()
+        .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */))
+        .SetSummary("Get shared tags")
+        .SetDescription("Extract the DICOM tags whose value is constant across all the child instances of "
+                        "the DICOM " + r + " whose Orthanc identifier is provided in the URL")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
+        .AddAnswerType(MimeType_Json, "JSON object containing the values of the DICOM tags")
+        .SetTruncatedJsonHttpGetSample(GetDocumentationSampleResource(t) + "/shared-tags", 5);
+      return;
+    }
+
     ServerContext& context = OrthancRestApi::GetContext(call);
     std::string publicId = call.GetUriComponent("id", "");
 
@@ -2385,7 +2402,7 @@
         .SetTag(GetResourceTypeText(start, true /* plural */, true /* upper case */))
         .SetSummary("Get child " + children)
         .SetDescription("Get detailed information about the child " + children + " of the DICOM " +
-                        resource + " of interest whose Orthanc identifier is provided in the URL")
+                        resource + " whose Orthanc identifier is provided in the URL")
         .SetUriArgument("id", "Orthanc identifier of the " + resource + " of interest")
         .AddAnswerType(MimeType_Json, "JSON array containing information about the child DICOM " + children)
         .SetTruncatedJsonHttpGetSample(GetDocumentationSampleResource(start) + "/" + children, 5);
@@ -2434,6 +2451,23 @@
 
   static void GetChildInstancesTags(RestApiGetCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str());
+      std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */);
+      call.GetDocumentation()
+        .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */))
+        .SetSummary("Get tags of instances")
+        .SetDescription("Get the tags of all the child instances of the DICOM " + r +
+                        " whose Orthanc identifier is provided in the URL")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
+        .SetHttpGetArgument("ignore-length", RestApiCallDocumentation::Type_JsonListOfStrings,
+                            "Also include the DICOM tags that are provided in this list, even if their associated value is long", false)
+        .AddAnswerType(MimeType_Json, "JSON object associating the Orthanc identifiers of the instances, with the values of their DICOM tags")
+        .SetTruncatedJsonHttpGetSample(GetDocumentationSampleResource(t) + "/instances-tags", 5);
+      return;
+    }
+
     ServerContext& context = OrthancRestApi::GetContext(call);
     std::string publicId = call.GetUriComponent("id", "");
     DicomToJsonFormat format = GetDicomFormat(call);
@@ -2486,7 +2520,7 @@
         .SetTag(GetResourceTypeText(start, true /* plural */, true /* upper case */))
         .SetSummary("Get parent " + parent)
         .SetDescription("Get detailed information about the parent " + parent + " of the DICOM " +
-                        resource + " of interest whose Orthanc identifier is provided in the URL")
+                        resource + " whose Orthanc identifier is provided in the URL")
         .SetUriArgument("id", "Orthanc identifier of the " + resource + " of interest")
         .AddAnswerType(MimeType_Json, "Information about the parent DICOM " + parent)
         .SetTruncatedJsonHttpGetSample(GetDocumentationSampleResource(start) + "/" + parent, 10);