changeset 4414:d928dfcacb4b

cont openapi
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 28 Dec 2020 14:46:51 +0100
parents 22a1352a0823
children b50410d0e98c
files OrthancFramework/Sources/RestApi/RestApi.cpp OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp OrthancFramework/Sources/RestApi/RestApiCallDocumentation.h OrthancServer/Sources/OrthancRestApi/OrthancRestChanges.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp
diffstat 5 files changed, 221 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/RestApi/RestApi.cpp	Mon Dec 28 13:08:00 2020 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApi.cpp	Mon Dec 28 14:46:51 2020 +0100
@@ -434,6 +434,10 @@
         std::string deleteTag_;
         std::string putTag_;
         std::string summary_;
+        bool        getDeprecated_;
+        bool        postDeprecated_;
+        bool        deleteDeprecated_;
+        bool        putDeprecated_;
         HttpMethod  summaryOrigin_;
 
       public:
@@ -442,12 +446,17 @@
           hasPost_(false),
           hasDelete_(false),
           hasPut_(false),
+          getDeprecated_(false),
+          postDeprecated_(false),
+          deleteDeprecated_(false),
+          putDeprecated_(false),
           summaryOrigin_(HttpMethod_Get)  // Dummy initialization
         {
         }
 
         void AddMethod(HttpMethod method,
-                       const std::string& tag)
+                       const std::string& tag,
+                       bool deprecated)
         {
           switch (method)
           {
@@ -459,6 +468,7 @@
               
               hasGet_ = true;
               getTag_ = tag;
+              getDeprecated_ = deprecated;
               break;
               
             case HttpMethod_Post:
@@ -469,6 +479,7 @@
               
               hasPost_ = true;
               postTag_ = tag;
+              postDeprecated_ = deprecated;
               break;
               
             case HttpMethod_Delete:
@@ -479,6 +490,7 @@
               
               hasDelete_ = true;
               deleteTag_ = tag;
+              deleteDeprecated_ = deprecated;
               break;
               
             case HttpMethod_Put:
@@ -489,6 +501,7 @@
               
               hasPut_ = true;
               putTag_ = tag;
+              putDeprecated_ = deprecated;
               break;
 
             default:
@@ -591,70 +604,68 @@
         {
           std::string p = uri;
           boost::replace_all(p, "/", "~1");
+
+          std::string verb;
+          std::string url;
           
           switch (method)
           {
             case HttpMethod_Get:
               if (hasGet_)
               {
-                if (openApiUrl.empty())
-                {
-                  return "GET";
-                }
-                else
-                {
-                  return ("`GET <" + openApiUrl + "#tag/" + FormatTag(getTag_) + "/paths/" + p + "/get>`__");
-                }
+                verb = (getDeprecated_ ? "(get)" : "GET");
+                url = openApiUrl + "#tag/" + FormatTag(getTag_) + "/paths/" + p + "/get";
               }
               break;
               
             case HttpMethod_Post:
               if (hasPost_)
               {
-                if (openApiUrl.empty())
-                {
-                  return "POST";
-                }
-                else
-                {
-                  return ("`POST <" + openApiUrl + "#tag/" + FormatTag(postTag_) + "/paths/" + p + "/post>`__");
-                }
+                verb = (postDeprecated_ ? "(post)" : "POST");
+                url = openApiUrl + "#tag/" + FormatTag(postTag_) + "/paths/" + p + "/post";
               }
               break;
               
             case HttpMethod_Delete:
               if (hasDelete_)
               {
-                if (openApiUrl.empty())
-                {
-                  return "DELETE";
-                }
-                else
-                {
-                  return ("`DELETE <" + openApiUrl + "#tag/" + FormatTag(deleteTag_) + "/paths/" + p + "/delete>`__");
-                }
+                verb = (deleteDeprecated_ ? "(delete)" : "DELETE");
+                url = openApiUrl + "#tag/" + FormatTag(deleteTag_) + "/paths/" + p + "/delete";
               }
               break;
               
             case HttpMethod_Put:
               if (hasPut_)
               {
-                if (openApiUrl.empty())
-                {
-                  return "GET";
-                }
-                else
-                {
-                  return ("`PUT <" + openApiUrl + "#tag/" + FormatTag(putTag_) + "/paths/" + p + "/put>`__");
-                }
+                verb = (putDeprecated_ ? "(put)" : "PUT");
+                url = openApiUrl + "#tag/" + FormatTag(putTag_) + "/paths/" + p + "/put";
               }
-              break;
+              break;              
 
             default:
               throw OrthancException(ErrorCode_InternalError);
           }
 
-          return "";
+          if (verb.empty())
+          {
+            return "";
+          }
+          else if (openApiUrl.empty())
+          {
+            return verb;
+          }
+          else
+          {
+            return "`" + verb + " <" + url + ">`__";
+          }
+        }
+
+        bool HasDeprecated() const
+        {
+          return ((hasGet_ && getDeprecated_) ||
+                  (hasPost_ && postDeprecated_) ||
+                  (hasDelete_ && deleteDeprecated_) ||
+                  (hasPut_ && putDeprecated_));
         }
       };
 
@@ -668,7 +679,7 @@
       {
         Path& path = paths_[ Toolbox::FlattenUri(call.GetFullUri()) ];
 
-        path.AddMethod(call.GetMethod(), call.GetDocumentation().GetTag());
+        path.AddMethod(call.GetMethod(), call.GetDocumentation().GetTag(), call.GetDocumentation().IsDeprecated());
 
         if (call.GetDocumentation().HasSummary())
         {
@@ -695,6 +706,12 @@
           target += it->second.Format(openApiUrl, HttpMethod_Post, it->first) + ",";
           target += it->second.Format(openApiUrl, HttpMethod_Delete, it->first) + ",";
           target += it->second.Format(openApiUrl, HttpMethod_Put, it->first) + ",";
+          
+          if (it->second.HasDeprecated())
+          {
+            target += "*(deprecated)* ";
+          }
+          
           target += it->second.GetSummary() + "\n";
         }        
       }
--- a/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp	Mon Dec 28 13:08:00 2020 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp	Mon Dec 28 14:46:51 2020 +0100
@@ -286,6 +286,8 @@
         target["description"] = summary_;
       }
 
+      target["deprecated"] = deprecated_;
+
       if (method_ == HttpMethod_Post ||
           method_ == HttpMethod_Put)
       {
--- a/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.h	Mon Dec 28 13:08:00 2020 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.h	Mon Dec 28 14:46:51 2020 +0100
@@ -105,12 +105,14 @@
     bool          hasSampleText_;
     std::string   sampleText_;
     Json::Value   sampleJson_;
+    bool          deprecated_;
 
   public:
     explicit RestApiCallDocumentation(HttpMethod method) :
       method_(method),
       hasSampleText_(false),
-      sampleJson_(Json::nullValue)
+      sampleJson_(Json::nullValue),
+      deprecated_(false)
     {
     }
     
@@ -195,5 +197,15 @@
     {
       return tag_;
     }
+
+    void SetDeprecated()
+    {
+      deprecated_ = true;
+    }
+
+    bool IsDeprecated() const
+    {
+      return deprecated_;
+    }
   };
 }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestChanges.cpp	Mon Dec 28 13:08:00 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestChanges.cpp	Mon Dec 28 14:46:51 2020 +0100
@@ -130,6 +130,23 @@
  
   static void GetExports(RestApiGetCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      call.GetDocumentation()
+        .SetTag("Tracking changes")
+        .SetSummary("List exports")
+        .SetDescription("For medical traceability, Orthanc can be configured to store a log of all the resources "
+                        "that have been exported to remote modalities. In auto-routing scenarios, it is important "
+                        "to prevent this log to grow indefinitely as incoming instances are routed. You can either "
+                        "disable this logging by setting the option `LogExportedResources` to `false` in the "
+                        "configuration file, or periodically clear this log by `DELETE`-ing this URI. This route "
+                        "might be removed in future versions of Orthanc.")
+        .SetHttpGetArgument("limit", RestApiCallDocumentation::Type_Number, "Limit the number of results", false)
+        .SetHttpGetArgument("since", RestApiCallDocumentation::Type_Number, "Show only the resources since the provided index", false)
+        .AddAnswerType(MimeType_Json, "The list of exports");
+      return;
+    }
+
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     int64_t since;
@@ -153,6 +170,15 @@
 
   static void DeleteExports(RestApiDeleteCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      call.GetDocumentation()
+        .SetTag("Tracking changes")
+        .SetSummary("Clear exports")
+        .SetDescription("Clear the full history stored in the exports log");
+      return;
+    }
+
     OrthancRestApi::GetIndex(call).DeleteExportedResources();
     call.GetOutput().AnswerBuffer("", MimeType_PlainText);
   }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Mon Dec 28 13:08:00 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Mon Dec 28 14:46:51 2020 +0100
@@ -513,6 +513,18 @@
   
   static void ListFrames(RestApiGetCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      call.GetDocumentation()
+        .SetTag("Instances")
+        .SetSummary("List available frames")
+        .SetDescription("List the frames that are available in the DICOM instance of interest")
+        .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest")
+        .AddAnswerType(MimeType_Json, "The list of the indices of the available frames")
+        .SetHttpGetSample("https://demo.orthanc-server.com/instances/7c92ce8e-bbf67ed2-ffa3b8c1-a3b35d94-7ff3ae26/frames", true);      
+      return;
+    }
+
     std::string publicId = call.GetUriComponent("id", "");
 
     unsigned int numberOfFrames;
@@ -1175,6 +1187,31 @@
 
   static void GetMatlabImage(RestApiGetCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      std::string description;
+      
+      if (call.HasUriComponent("frame"))
+      {
+        description = "Decode one frame of interest from the given DICOM instance";
+        call.GetDocumentation()
+          .SetUriArgument("frame", RestApiCallDocumentation::Type_Number, "Index of the frame (starts at `0`)");
+      }
+      else
+      {
+        description = "Decode the first frame of the given DICOM instance.";
+      }
+
+      call.GetDocumentation()
+        .SetTag("Instances")
+        .SetSummary("Decode frame for Matlab")
+        .SetDescription(description + ", and export this frame as a Octave/Matlab matrix to be imported with `eval()`: "
+                        "https://book.orthanc-server.com/faq/matlab.html")
+        .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest")
+        .AddAnswerType(MimeType_PlainText, "Octave/Matlab matrix");
+      return;
+    }
+
     Semaphore::Locker locker(throttlingSemaphore_);
         
     ServerContext& context = OrthancRestApi::GetContext(call);
@@ -1325,6 +1362,23 @@
 
   static void ListMetadata(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("List metadata")
+        .SetDescription("Get the list of metadata that are associated with the given " + r)
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
+        .SetHttpGetArgument("expand", RestApiCallDocumentation::Type_String,
+                            "If present, also retrieve the value of the individual metadata", false)
+        .AddAnswerType(MimeType_Json, "JSON array containing the names of the available metadata, "
+                       "or JSON associative array mapping metadata to their values (if `expand` argument is provided)")
+        .SetHttpGetSample(GetDocumentationSampleResource(t) + "/metadata", true);
+      return;
+    }
+
     CheckValidResourceType(call);
     
     std::string publicId = call.GetUriComponent("id", "");
@@ -1362,6 +1416,20 @@
 
   static void GetMetadata(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 metadata")
+        .SetDescription("Get the value of a metadata that is associated with the given " + r)
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
+        .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)")
+        .AddAnswerType(MimeType_PlainText, "Value of the metadata");
+      return;
+    }
+
     CheckValidResourceType(call);
     
     std::string publicId = call.GetUriComponent("id", "");
@@ -1378,6 +1446,20 @@
 
   static void DeleteMetadata(RestApiDeleteCall& 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("Delete metadata")
+        .SetDescription("Delete some metadata associated with the given DICOM " + r +
+                        ". This call will fail if trying to delete a system metadata (i.e. whose index is < 1024).")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
+        .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)");
+      return;
+    }
+
     CheckValidResourceType(call);
 
     std::string publicId = call.GetUriComponent("id", "");
@@ -1398,6 +1480,21 @@
 
   static void SetMetadata(RestApiPutCall& 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("Set metadata")
+        .SetDescription("Set the value of some metadata in the given DICOM " + r +
+                        ". This call will fail if trying to modify a system metadata (i.e. whose index is < 1024).")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
+        .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)")
+        .AddRequestType(MimeType_PlainText, "String value of the metadata");
+      return;
+    }
+
     CheckValidResourceType(call);
 
     std::string publicId = call.GetUriComponent("id", "");
@@ -1477,10 +1574,10 @@
       std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */);
       call.GetDocumentation()
         .SetTag("Other")
-        .SetSummary("List of operations on attachments")
-        .SetDescription("Get the list of operations that are available for attachments associated with the given " + r)
+        .SetSummary("List operations on attachments")
+        .SetDescription("Get the list of the operations that are available for attachments associated with the given " + r)
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
         .AddAnswerType(MimeType_Json, "List of the available operations")
         .SetHttpGetSample("https://demo.orthanc-server.com/instances/d94d9a03-3003b047-a4affc69-322313b2-680530a2/attachments/dicom", true);
       return;
@@ -1535,7 +1632,7 @@
         .SetDescription("Get the (binary) content of one attachment associated with the given " + r +
                         std::string(uncompress ? "" : ". The attachment will not be decompressed if `StorageCompression` if `true`."))
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
         .AddAnswerType(MimeType_Binary, "The attachment");
       return;
     }
@@ -1572,7 +1669,7 @@
         .SetSummary("Get size of attachment")
         .SetDescription("Get the size of one attachment associated with the given " + r)
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
         .AddAnswerType(MimeType_PlainText, "The size of the attachment");
       return;
     }
@@ -1597,7 +1694,7 @@
         .SetDescription("Get the size of one attachment associated with the given " + r + ", as stored on the disk. "
                         "This is different from `.../size` iff `EnableStorage` is `true`.")
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
         .AddAnswerType(MimeType_PlainText, "The size of the attachment, as stored on the disk");
       return;
     }
@@ -1621,7 +1718,7 @@
         .SetSummary("Get MD5 of attachment")
         .SetDescription("Get the MD5 hash of one attachment associated with the given " + r)
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
         .AddAnswerType(MimeType_PlainText, "The MD5 of the attachment");
       return;
     }
@@ -1647,7 +1744,7 @@
         .SetDescription("Get the MD5 hash of one attachment associated with the given " + r + ", as stored on the disk. "
                         "This is different from `.../md5` iff `EnableStorage` is `true`.")
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
         .AddAnswerType(MimeType_PlainText, "The MD5 of the attachment, as stored on the disk");
       return;
     }
@@ -1672,7 +1769,7 @@
         .SetSummary("Verify attachment")
         .SetDescription("Verify that the attachment is not corrupted, by validating its MD5 hash")
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
         .AddAnswerType(MimeType_Json, "On success, a valid JSON object is returned");
       return;
     }
@@ -1731,6 +1828,22 @@
 
   static void UploadAttachment(RestApiPutCall& 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("Set attachment")
+        .SetDescription("Attach a file to the given DICOM " + r +
+                        ". This call will fail if trying to modify a system attachment (i.e. whose index is < 1024).")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
+        .AddRequestType(MimeType_Binary, "Binary data containing the attachment")
+        .AddAnswerType(MimeType_Json, "Empty JSON object in the case of a success");
+      return;
+    }
+
     ServerContext& context = OrthancRestApi::GetContext(call);
     CheckValidResourceType(call);
  
@@ -1759,9 +1872,10 @@
       call.GetDocumentation()
         .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */))
         .SetSummary("Delete attachment")
-        .SetDescription("Delete an attachment associated with the given DICOM " + r)
+        .SetDescription("Delete an attachment associated with the given DICOM " + r +
+                        ". This call will fail if trying to delete a system attachment (i.e. whose index is < 1024).")
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment");
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)");
       return;
     }
 
@@ -1818,7 +1932,7 @@
         .SetSummary(compression == CompressionType_None ? "Uncompress attachment" : "Compress attachment")
         .SetDescription("Change the compression scheme that is used to store an attachment.")
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment");
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)");
       return;
     }
 
@@ -1844,7 +1958,7 @@
         .SetSummary("Is attachment compressed?")
         .SetDescription("Test whether the attachment has been stored as a compressed file on the disk.")
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetUriArgument("name", "The name of the attachment")
+        .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
         .AddAnswerType(MimeType_PlainText, "`0` if the attachment was stored uncompressed, `1` if it was compressed");
       return;
     }