# HG changeset patch # User Sebastien Jodogne # Date 1609163211 -3600 # Node ID d928dfcacb4b26cb393a9a4c39092e08f3665911 # Parent 22a1352a082350ec16d8b338582c980097c97859 cont openapi diff -r 22a1352a0823 -r d928dfcacb4b OrthancFramework/Sources/RestApi/RestApi.cpp --- 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"; } } diff -r 22a1352a0823 -r d928dfcacb4b OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp --- 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) { diff -r 22a1352a0823 -r d928dfcacb4b OrthancFramework/Sources/RestApi/RestApiCallDocumentation.h --- 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_; + } }; } diff -r 22a1352a0823 -r d928dfcacb4b OrthancServer/Sources/OrthancRestApi/OrthancRestChanges.cpp --- 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); } diff -r 22a1352a0823 -r d928dfcacb4b OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- 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; }