changeset 4424:83371ccdfe80

openapi documentation is now completed
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Dec 2020 19:28:53 +0100
parents 2a69b58ff3ac
children d6cb9e91a3d6
files OrthancFramework/Sources/Enumerations.h OrthancFramework/Sources/RestApi/RestApi.cpp OrthancFramework/Sources/RestApi/RestApiCall.cpp OrthancFramework/Sources/RestApi/RestApiCall.h OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp OrthancServer/Resources/Configuration.json OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp
diffstat 10 files changed, 187 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/Enumerations.h	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancFramework/Sources/Enumerations.h	Tue Dec 29 19:28:53 2020 +0100
@@ -596,7 +596,9 @@
     // Some predefined combinations
     DicomFromJsonFlags_None = 0
   };
-  
+
+  // If adding a new DICOM version below, update the
+  // "DeidentifyLogsDicomVersion" configuration option
   enum DicomVersion
   {
     DicomVersion_2008,
--- a/OrthancFramework/Sources/RestApi/RestApi.cpp	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApi.cpp	Tue Dec 29 19:28:53 2020 +0100
@@ -874,8 +874,8 @@
   {    
     call.GetDocumentation()
       .SetTag("Other")
-      .SetSummary("List of operations")
-      .SetDescription("List the available operations under URI: " + call.FlattenUri())
+      .SetSummary("List operations")
+      .SetDescription("List the available operations under URI `" + call.FlattenUri() + "`")
       .AddAnswerType(MimeType_Json, "List of the available operations");
 
     RestApi& context = call.GetContext();
@@ -886,12 +886,19 @@
       if (call.IsDocumentation())
       {
         call.GetDocumentation().SetSample(directory);
+
+        std::set<std::string> c;
+        call.GetUriComponentsNames(c);
+        for (std::set<std::string>::const_iterator it = c.begin(); it != c.end(); ++it)
+        {
+          call.GetDocumentation().SetUriArgument(*it, RestApiCallDocumentation::Type_String, "");
+        }    
       }
       else
       {
         call.GetOutput().AnswerJson(directory);
       }
-    }    
+    }
   }
 
 
--- a/OrthancFramework/Sources/RestApi/RestApiCall.cpp	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiCall.cpp	Tue Dec 29 19:28:53 2020 +0100
@@ -25,6 +25,18 @@
 
 namespace Orthanc
 {
+  void RestApiCall::GetUriComponentsNames(std::set<std::string>& components) const
+  {
+    components.clear();
+    
+    for (HttpToolbox::Arguments::const_iterator it = uriComponents_.begin();
+         it != uriComponents_.end(); ++it)
+    {
+      components.insert(it->first);
+    }
+  }
+  
+
   std::string RestApiCall::FlattenUri() const
   {
     std::string s = "/";
--- a/OrthancFramework/Sources/RestApi/RestApiCall.h	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiCall.h	Tue Dec 29 19:28:53 2020 +0100
@@ -28,6 +28,7 @@
 #include "RestApiOutput.h"
 
 #include <boost/noncopyable.hpp>
+#include <set>
 
 namespace Orthanc
 {
@@ -92,6 +93,8 @@
       return trailing_;
     }
 
+    void GetUriComponentsNames(std::set<std::string>& components) const;
+
     bool HasUriComponent(const std::string& name) const
     {
       return (uriComponents_.find(name) != uriComponents_.end());
--- a/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp	Tue Dec 29 19:28:53 2020 +0100
@@ -179,8 +179,6 @@
   void RestApiCallDocumentation::SetHttpGetSample(const std::string& url,
                                                   bool isJson)
   {
-    //return;  // TODO -REMOVE
-    
 #if ORTHANC_ENABLE_CURL == 1
     HttpClient client;
     client.SetUrl(url);
@@ -480,14 +478,7 @@
       {
         if (uriArguments_.find(*it) == uriArguments_.end())
         {
-          LOG(WARNING) << "Adding missing expected URI argument: " << *it;
-          Json::Value p = Json::objectValue;
-          p["name"] = *it;
-          p["in"] = "path";
-          p["required"] = true;
-          p["schema"]["type"] = "string";
-          p["description"] = "";
-          parameters.append(p);
+          throw OrthancException(ErrorCode_InternalError, "Missing URI argument: " + *it);
         }
       }
 
--- a/OrthancServer/Resources/Configuration.json	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancServer/Resources/Configuration.json	Tue Dec 29 19:28:53 2020 +0100
@@ -672,9 +672,9 @@
   // Table E.1-1 of the DICOM standard (new in Orthanc 1.8.2)
   "DeidentifyLogs" : true,
 
-  // If "DeidentifyLogs" is true, this sets the DICOM standard
-  // to follow for the deidentification/anonymization of the query
-  // contents. Possible values are those that are specified in the
-  // definition of Orthanc::StringToDicomVersion (new in Orthanc 1.8.2)
+  // If "DeidentifyLogs" is true, this sets the DICOM standard to
+  // follow for the deidentification/anonymization of the query
+  // contents. Possible values are "2008" and "2017c" (new in Orthanc
+  // 1.8.2)
   "DeidentifyLogsDicomVersion" : "2017c"
 }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue Dec 29 19:28:53 2020 +0100
@@ -58,6 +58,54 @@
   }
 
 
+  static void DocumentModifyOptions(RestApiPostCall& call)
+  {
+    // Check out "DicomModification::ParseModifyRequest()"
+    call.GetDocumentation()
+      .SetRequestField("Transcode", RestApiCallDocumentation::Type_String,
+                       "Transcode the DICOM instances to the provided DICOM transfer syntax: "
+                       "https://book.orthanc-server.com/faq/transcoding.html", false)
+      .SetRequestField("Force", RestApiCallDocumentation::Type_Boolean,
+                       "Allow the modification of tags related to DICOM identifiers, at the risk of "
+                       "breaking the DICOM model of the real world", false)
+      .SetRequestField("RemovePrivateTags", RestApiCallDocumentation::Type_Boolean,
+                       "Remove the private tags from the DICOM instances (defaults to `false`)", false)
+      .SetRequestField("Replace", RestApiCallDocumentation::Type_JsonObject,
+                       "Associative array to change the value of some DICOM tags in the DICOM instances", false)
+      .SetRequestField("Remove", RestApiCallDocumentation::Type_JsonListOfStrings,
+                       "List of tags that must be removed from the DICOM instances", false)
+      .SetRequestField("Keep", RestApiCallDocumentation::Type_JsonListOfStrings,
+                       "Keep the original value of the specified tags, to be chosen among the `StudyInstanceUID`, "
+                       "`SeriesInstanceUID` and `SOPInstanceUID` tags. Avoid this feature as much as possible, "
+                       "as this breaks the DICOM model of the real world.", false)
+      .SetRequestField("PrivateCreator", RestApiCallDocumentation::Type_String,
+                       "The private creator to be used for private tags in `Replace`", false);
+  }
+
+
+  static void DocumentAnonymizationOptions(RestApiPostCall& call)
+  {
+    // Check out "DicomModification::ParseAnonymizationRequest()"
+    call.GetDocumentation()
+      .SetRequestField("Force", RestApiCallDocumentation::Type_Boolean,
+                       "Allow the modification of tags related to DICOM identifiers, at the risk of "
+                       "breaking the DICOM model of the real world", false)
+      .SetRequestField("DicomVersion", RestApiCallDocumentation::Type_String,
+                       "Version of the DICOM standard to be used for anonymization. Check out "
+                       "configuration option `DeidentifyLogsDicomVersion` for possible values.", false)
+      .SetRequestField("KeepPrivateTags", RestApiCallDocumentation::Type_Boolean,
+                       "Keep the private tags from the DICOM instances (defaults to `false`)", false)
+      .SetRequestField("Replace", RestApiCallDocumentation::Type_JsonObject,
+                       "Associative array to change the value of some DICOM tags in the DICOM instances", false)
+      .SetRequestField("Remove", RestApiCallDocumentation::Type_JsonListOfStrings,
+                       "List of additional tags to be removed from the DICOM instances", false)
+      .SetRequestField("Keep", RestApiCallDocumentation::Type_JsonListOfStrings,
+                       "List of DICOM tags whose value must not be destroyed by the anonymization", false)
+      .SetRequestField("PrivateCreator", RestApiCallDocumentation::Type_String,
+                       "The private creator to be used for private tags in `Replace`", false);
+  }
+
+
   static void ParseModifyRequest(Json::Value& request,
                                  DicomModification& target,
                                  const RestApiPostCall& call)
@@ -159,6 +207,19 @@
 
   static void ModifyInstance(RestApiPostCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      DocumentModifyOptions(call);
+      call.GetDocumentation()
+        .SetTag("Instance")
+        .SetSummary("Modify instance")
+        .SetDescription("Download a modified version of the DICOM instance whose Orthanc identifier is provided in the URL: "
+                        "https://book.orthanc-server.com/users/anonymization.html#modification-of-a-single-instance")
+        .SetUriArgument("id", "Orthanc identifier of the instance of interest")
+        .AddAnswerType(MimeType_Dicom, "The modified DICOM instance");
+      return;
+    }
+
     DicomModification modification;
     modification.SetAllowManualIdentifiers(true);
 
@@ -207,6 +268,19 @@
 
   static void AnonymizeInstance(RestApiPostCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      DocumentAnonymizationOptions(call);
+      call.GetDocumentation()
+        .SetTag("Instance")
+        .SetSummary("Anonymize instance")
+        .SetDescription("Download an anonymized version of the DICOM instance whose Orthanc identifier is provided in the URL: "
+                        "https://book.orthanc-server.com/users/anonymization.html#anonymization-of-a-single-instance")
+        .SetUriArgument("id", "Orthanc identifier of the instance of interest")
+        .AddAnswerType(MimeType_Dicom, "The anonymized DICOM instance");
+      return;
+    }
+
     DicomModification modification;
     modification.SetAllowManualIdentifiers(true);
 
@@ -260,6 +334,22 @@
   template <enum ResourceType resourceType>
   static void ModifyResource(RestApiPostCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      OrthancRestApi::DocumentSubmitCommandsJob(call);
+      DocumentModifyOptions(call);
+      const std::string r = GetResourceTypeText(resourceType, false /* plural */, false /* lower case */);      
+      call.GetDocumentation()
+        .SetTag(GetResourceTypeText(resourceType, true /* plural */, true /* upper case */))
+        .SetSummary("Modify " + r)
+        .SetDescription("Start a job that will modify all the DICOM instances within the " + r +
+                        " whose identifier is provided in the URL. The modified DICOM instances will be "
+                        "stored into a brand new " + r + ", whose Orthanc identifiers will be returned by the job. "
+                        "https://book.orthanc-server.com/users/anonymization.html#modification-of-studies-or-series")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest");
+      return;
+    }
+    
     std::unique_ptr<DicomModification> modification(new DicomModification);
 
     Json::Value body;
@@ -275,6 +365,22 @@
   template <enum ResourceType resourceType>
   static void AnonymizeResource(RestApiPostCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      OrthancRestApi::DocumentSubmitCommandsJob(call);
+      DocumentAnonymizationOptions(call);
+      const std::string r = GetResourceTypeText(resourceType, false /* plural */, false /* lower case */);      
+      call.GetDocumentation()
+        .SetTag(GetResourceTypeText(resourceType, true /* plural */, true /* upper case */))
+        .SetSummary("Anonymize " + r)
+        .SetDescription("Start a job that will anonymize all the DICOM instances within the " + r +
+                        " whose identifier is provided in the URL. The modified DICOM instances will be "
+                        "stored into a brand new " + r + ", whose Orthanc identifiers will be returned by the job. "
+                        "https://book.orthanc-server.com/users/anonymization.html#anonymization-of-patients-studies-or-series")
+        .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest");
+      return;
+    }
+
     std::unique_ptr<DicomModification> modification(new DicomModification);
 
     Json::Value body;
@@ -754,6 +860,32 @@
 
   static void SplitStudy(RestApiPostCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      OrthancRestApi::DocumentSubmitCommandsJob(call);
+      call.GetDocumentation()
+        .SetTag("Studies")
+        .SetSummary("Split study")
+        .SetDescription("Start a new job so as to split the DICOM study whose Orthanc identifier is provided in the URL, "
+                        "by taking some of its children series out of it and putting them into a brand new study (this "
+                        "new study is created by setting the `StudyInstanceUID` tag to a random identifier): "
+                        "https://book.orthanc-server.com/users/anonymization.html#splitting")
+        .SetUriArgument("id", "Orthanc identifier of the study of interest")
+        .SetRequestField("Series", RestApiCallDocumentation::Type_JsonListOfStrings,
+                         "The list of series to be separated from the parent study (mandatory option). "
+                         "These series must all be children of the same source study, that is specified in the URI.", true)
+        .SetRequestField("Replace", RestApiCallDocumentation::Type_JsonObject,
+                         "Associative array to change the value of some DICOM tags in the new study. "
+                         "These tags must be part of the \"Patient Module Attributes\" or the \"General Study "
+                         "Module Attributes\", as specified by the DICOM 2011 standard in Tables C.7-1 and C.7-3.", false)
+        .SetRequestField("Remove", RestApiCallDocumentation::Type_JsonListOfStrings,
+                         "List of tags that must be removed in the new study (from the same modules as in the `Replace` option)", false)
+        .SetRequestField("KeepSource", RestApiCallDocumentation::Type_Boolean,
+                         "If set to `true`, instructs Orthanc to keep a copy of the original series in the source study. "
+                         "By default, the original series are deleted from Orthanc.", false);
+      return;
+    }
+    
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     Json::Value request;
@@ -833,6 +965,24 @@
 
   static void MergeStudy(RestApiPostCall& call)
   {
+    if (call.IsDocumentation())
+    {
+      OrthancRestApi::DocumentSubmitCommandsJob(call);
+      call.GetDocumentation()
+        .SetTag("Studies")
+        .SetSummary("Merge study")
+        .SetDescription("Start a new job so as to move some DICOM series into the DICOM study whose Orthanc identifier "
+                        "is provided in the URL: https://book.orthanc-server.com/users/anonymization.html#merging")
+        .SetUriArgument("id", "Orthanc identifier of the study of interest")
+        .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings,
+                         "The list of DICOM resources (patients, studies, series, and/or instances) to be merged "
+                         "into the study of interest (mandatory option)", true)
+        .SetRequestField("KeepSource", RestApiCallDocumentation::Type_Boolean,
+                         "If set to `true`, instructs Orthanc to keep a copy of the original resources in their source study. "
+                         "By default, the original resources are deleted from Orthanc.", false);
+      return;
+    }
+
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     Json::Value request;
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp	Tue Dec 29 19:28:53 2020 +0100
@@ -271,7 +271,6 @@
     Register("/tools", RestApi::AutoListChildren);
     Register("/tools/reset", ResetOrthanc);
     Register("/tools/shutdown", ShutdownOrthanc);
-    Register("/instances/{id}/frames/{frame}", RestApi::AutoListChildren);
   }
 
 
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Tue Dec 29 19:28:53 2020 +0100
@@ -1270,6 +1270,7 @@
                         "bypassing image decoding. This is notably useful to access the source files "
                         "in compressed transfer syntaxes." +
                         std::string(GzipCompression ? " The image is compressed using gzip" : ""))
+        .SetUriArgument("id", "Orthanc identifier of the instance of interest")
         .SetUriArgument("frame", RestApiCallDocumentation::Type_Number, "Index of the frame (starts at `0`)");
 
       if (GzipCompression)
@@ -2917,6 +2918,7 @@
     Register("/instances/{id}/simplified-tags", GetInstanceTags<DicomToJsonFormat_Human>);
     Register("/instances/{id}/frames", ListFrames);
 
+    Register("/instances/{id}/frames/{frame}", RestApi::AutoListChildren);
     Register("/instances/{id}/frames/{frame}/preview", GetImage<ImageExtractionMode_Preview>);
     Register("/instances/{id}/frames/{frame}/rendered", GetRenderedFrame);
     Register("/instances/{id}/frames/{frame}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp	Tue Dec 29 16:51:28 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp	Tue Dec 29 19:28:53 2020 +0100
@@ -672,7 +672,8 @@
       call.GetDocumentation()
         .SetTag("System")
         .SetSummary("Get usage metrics")
-        .SetDescription("Get usage metrics of Orthanc in the Prometheus file format (OpenMetrics)")
+        .SetDescription("Get usage metrics of Orthanc in the Prometheus file format (OpenMetrics): "
+                        "https://book.orthanc-server.com/users/advanced-rest.html#instrumentation-with-prometheus")
         .SetHttpGetSample("https://demo.orthanc-server.com/tools/metrics-prometheus", false);
       return;
     }