# HG changeset patch # User Sebastien Jodogne # Date 1608799050 -3600 # Node ID 5466f336b09f1b8e6bed507d1774b0bcfd9ff0d6 # Parent f34634916d8cc6fb6b926ff90662b19a72675e93 gathering statistics about progress of api documentation diff -r f34634916d8c -r 5466f336b09f OrthancFramework/Sources/RestApi/RestApi.cpp --- a/OrthancFramework/Sources/RestApi/RestApi.cpp Thu Dec 24 08:59:45 2020 +0100 +++ b/OrthancFramework/Sources/RestApi/RestApi.cpp Thu Dec 24 09:37:30 2020 +0100 @@ -27,6 +27,7 @@ #include "../Logging.h" #include "../OrthancException.h" +#include #include // To define "_exit()" under Windows #include @@ -131,10 +132,15 @@ private: RestApi& restApi_; Json::Value paths_; + size_t successPathsCount_; + size_t totalPathsCount_; public: explicit OpenApiVisitor(RestApi& restApi) : - restApi_(restApi) + restApi_(restApi), + paths_(Json::objectValue), + successPathsCount_(0), + totalPathsCount_(0) { } @@ -144,7 +150,11 @@ const HttpToolbox::Arguments& components, const UriComponents& trailing) { - const std::string path = Toolbox::FlattenUri(uri); + std::string path = Toolbox::FlattenUri(uri); + if (hasTrailing) + { + path += "/{...}"; + } if (paths_.isMember(path)) { @@ -160,8 +170,15 @@ uriArguments.insert(it->first.c_str()); } + if (hasTrailing) + { + uriArguments.insert("..."); + } + if (resource.HasHandler(HttpMethod_Get)) { + totalPathsCount_ ++; + StringHttpOutput o1; HttpOutput o2(o1, false); RestApiOutput o3(o2, HttpMethod_Get); @@ -189,6 +206,7 @@ if (ok) { paths_[path]["get"] = v; + successPathsCount_ ++; } else { @@ -198,6 +216,8 @@ if (resource.HasHandler(HttpMethod_Post)) { + totalPathsCount_ ++; + StringHttpOutput o1; HttpOutput o2(o1, false); RestApiOutput o3(o2, HttpMethod_Post); @@ -224,6 +244,7 @@ if (ok) { paths_[path]["post"] = v; + successPathsCount_ ++; } else { @@ -233,6 +254,8 @@ if (resource.HasHandler(HttpMethod_Delete)) { + totalPathsCount_ ++; + StringHttpOutput o1; HttpOutput o2(o1, false); RestApiOutput o3(o2, HttpMethod_Delete); @@ -259,6 +282,7 @@ if (ok) { paths_[path]["delete"] = v; + successPathsCount_ ++; } else { @@ -268,6 +292,8 @@ if (resource.HasHandler(HttpMethod_Put)) { + totalPathsCount_ ++; + StringHttpOutput o1; HttpOutput o2(o1, false); RestApiOutput o3(o2, HttpMethod_Put); @@ -294,6 +320,7 @@ if (ok) { paths_[path]["put"] = v; + successPathsCount_ ++; } else { @@ -309,6 +336,16 @@ { return paths_; } + + size_t GetSuccessPathsCount() const + { + return successPathsCount_; + } + + size_t GetTotalPathsCount() const + { + return totalPathsCount_; + } }; } @@ -495,5 +532,18 @@ target["openapi"] = "3.0.0"; target["servers"] = Json::arrayValue; target["paths"] = visitor.GetPaths(); + + assert(visitor.GetSuccessPathsCount() <= visitor.GetTotalPathsCount()); + size_t total = visitor.GetTotalPathsCount(); + if (total == 0) + { + total = 1; // Avoid division by zero + } + float coverage = (100.0f * static_cast(visitor.GetSuccessPathsCount()) / + static_cast(total)); + + LOG(WARNING) << "The documentation of the REST API contains " << visitor.GetSuccessPathsCount() + << " paths over a total of " << visitor.GetTotalPathsCount() << " paths " + << "(" << static_cast(boost::math::iround(coverage)) << "%)"; } } diff -r f34634916d8c -r 5466f336b09f OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp --- a/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp Thu Dec 24 08:59:45 2020 +0100 +++ b/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp Thu Dec 24 09:37:30 2020 +0100 @@ -103,6 +103,7 @@ RestApiCallDocumentation& RestApiCallDocumentation::SetUriArgument(const std::string& name, + Type type, const std::string& description) { if (uriArguments_.find(name) != uriArguments_.end()) @@ -111,7 +112,7 @@ } else { - uriArguments_[name] = Parameter(Type_String, description, true); + uriArguments_[name] = Parameter(type, description, true); return *this; } } @@ -318,13 +319,24 @@ } } - if (sampleJson_.type() != Json::nullValue) + for (AllowedTypes::const_iterator it = answerTypes_.begin(); + it != answerTypes_.end(); ++it) { - target["responses"]["200"]["content"][EnumerationToString(MimeType_Json)]["schema"]["example"] = sampleJson_; - } - else if (answerTypes_.find(MimeType_Json) != answerTypes_.end()) - { - target["responses"]["200"]["content"][EnumerationToString(MimeType_Json)]["examples"] = Json::objectValue; + if (it->first == MimeType_Json && + sampleJson_.type() != Json::nullValue) + { + target["responses"]["200"]["content"][EnumerationToString(MimeType_Json)]["schema"]["example"] = sampleJson_; + } + else if (it->first == MimeType_PlainText && + hasSampleText_) + { + // Handled below + } + else + { + // No sample for this MIME type + target["responses"]["200"]["content"][EnumerationToString(it->first)]["examples"] = Json::objectValue; + } } if (hasSampleText_) diff -r f34634916d8c -r 5466f336b09f OrthancFramework/Sources/RestApi/RestApiCallDocumentation.h --- a/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.h Thu Dec 24 08:59:45 2020 +0100 +++ b/OrthancFramework/Sources/RestApi/RestApiCallDocumentation.h Thu Dec 24 09:37:30 2020 +0100 @@ -144,8 +144,15 @@ const std::string& description); RestApiCallDocumentation& SetUriArgument(const std::string& name, + Type type, const std::string& description); + RestApiCallDocumentation& SetUriArgument(const std::string& name, + const std::string& description) + { + return SetUriArgument(name, Type_String, description); + } + bool HasUriArgument(const std::string& name) const { return (uriArguments_.find(name) != uriArguments_.end()); diff -r f34634916d8c -r 5466f336b09f OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Thu Dec 24 08:59:45 2020 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Thu Dec 24 09:37:30 2020 +0100 @@ -336,6 +336,20 @@ static void GetInstanceFile(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Instances") + .SetSummary("Download DICOM") + .SetDescription("Download one DICOM instance") + .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest") + .SetHttpHeader("Accept", "This HTTP header can be set to retrieve the DICOM instance in DICOMweb format") + .AddAnswerType(MimeType_Dicom, "The DICOM instance") + .AddAnswerType(MimeType_DicomWebJson, "The DICOM instance, in DICOMweb JSON format") + .AddAnswerType(MimeType_DicomWebXml, "The DICOM instance, in DICOMweb XML format"); + return; + } + ServerContext& context = OrthancRestApi::GetContext(call); std::string publicId = call.GetUriComponent("id", ""); @@ -1570,6 +1584,21 @@ static void GetRawContent(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Instances") + .SetSummary("Get raw tag") + .SetDescription("Get the raw content of one DICOM tag in the hierarchy of DICOM dataset") + .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest") + .SetUriArgument("...", "Path to the DICOM tag. This is the interleaving of one DICOM tag, possibly followed " + "by an index for sequences. Sequences are accessible as, for instance, `/0008-1140/1/0008-1150`") + .AddAnswerType(MimeType_Binary, "The raw value of the tag of intereset " + "(binary data, whose memory layout depends on the underlying transfer syntax), " + "or JSON array containing the list of available tags if accessing a dataset"); + return; + } + std::string id = call.GetUriComponent("id", ""); ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), id);