# HG changeset patch # User Sebastien Jodogne # Date 1609266533 -3600 # Node ID 83371ccdfe80a307476b2a552c6dbd2e0861f37f # Parent 2a69b58ff3ac04d553eb8d3c9bb4f17bc420145d openapi documentation is now completed diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancFramework/Sources/Enumerations.h --- 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, diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancFramework/Sources/RestApi/RestApi.cpp --- 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 c; + call.GetUriComponentsNames(c); + for (std::set::const_iterator it = c.begin(); it != c.end(); ++it) + { + call.GetDocumentation().SetUriArgument(*it, RestApiCallDocumentation::Type_String, ""); + } } else { call.GetOutput().AnswerJson(directory); } - } + } } diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancFramework/Sources/RestApi/RestApiCall.cpp --- 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& 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 = "/"; diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancFramework/Sources/RestApi/RestApiCall.h --- 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 +#include namespace Orthanc { @@ -92,6 +93,8 @@ return trailing_; } + void GetUriComponentsNames(std::set& components) const; + bool HasUriComponent(const std::string& name) const { return (uriComponents_.find(name) != uriComponents_.end()); diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp --- 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); } } diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancServer/Resources/Configuration.json --- 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" } diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp --- 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 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 modification(new DicomModification); Json::Value body; @@ -275,6 +365,22 @@ template 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 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; diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp --- 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); } diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- 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); Register("/instances/{id}/frames", ListFrames); + Register("/instances/{id}/frames/{frame}", RestApi::AutoListChildren); Register("/instances/{id}/frames/{frame}/preview", GetImage); Register("/instances/{id}/frames/{frame}/rendered", GetRenderedFrame); Register("/instances/{id}/frames/{frame}/image-uint8", GetImage); diff -r 2a69b58ff3ac -r 83371ccdfe80 OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp --- 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; }