# HG changeset patch # User Sebastien Jodogne # Date 1609239222 -3600 # Node ID cd96c807ca3de2cecfbfb3fb8fa0fafe5ef713e9 # Parent 9d6fa3da8f00b2827fd007c8b75f45fb96ba5e97 cont openapi diff -r 9d6fa3da8f00 -r cd96c807ca3d OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Tue Dec 29 10:24:17 2020 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Tue Dec 29 11:53:42 2020 +0100 @@ -430,4 +430,30 @@ SubmitGenericJob(call, raii.release(), isDefaultSynchronous, body); } + + + void OrthancRestApi::DocumentSubmitGenericJob(RestApiPostCall& call) + { + call.GetDocumentation() + .SetRequestField(KEY_SYNCHRONOUS, RestApiCallDocumentation::Type_Boolean, + "If `true`, run the job in synchronous mode, which means that the HTTP answer will directly " + "contain the result of the job. This is the default, easy behavior, but it is *not* desirable for " + "long jobs, as it might lead to network timeouts.", false) + .SetRequestField(KEY_ASYNCHRONOUS, RestApiCallDocumentation::Type_Boolean, + "If `true`, run the job in asynchronous mode, which means that the REST API call will immediately " + "return, reporting the identifier of a job. Prefer this flavor wherever possible.", false) + .SetRequestField(KEY_PRIORITY, RestApiCallDocumentation::Type_Number, + "In asynchronous mode, the priority of the job. The lower the value, the higher the priority.", false) + .SetAnswerField("ID", RestApiCallDocumentation::Type_String, "In asynchronous mode, identifier of the job") + .SetAnswerField("Path", RestApiCallDocumentation::Type_String, "In asynchronous mode, path to access the job in the REST API"); + } + + + void OrthancRestApi::DocumentSubmitCommandsJob(RestApiPostCall& call) + { + DocumentSubmitGenericJob(call); + call.GetDocumentation() + .SetRequestField(KEY_PERMISSIVE, RestApiCallDocumentation::Type_Boolean, + "If `true`, ignore errors during the individual steps of the job.", false); + } } diff -r 9d6fa3da8f00 -r cd96c807ca3d OrthancServer/Sources/OrthancRestApi/OrthancRestApi.h --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.h Tue Dec 29 10:24:17 2020 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.h Tue Dec 29 11:53:42 2020 +0100 @@ -140,5 +140,9 @@ SetOfCommandsJob* job, bool isDefaultSynchronous, const Json::Value& body) const; + + static void DocumentSubmitGenericJob(RestApiPostCall& call); + + static void DocumentSubmitCommandsJob(RestApiPostCall& call); }; } diff -r 9d6fa3da8f00 -r cd96c807ca3d OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp Tue Dec 29 10:24:17 2020 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp Tue Dec 29 11:53:42 2020 +0100 @@ -568,6 +568,19 @@ static void ListQueries(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("List query/retrieve operations") + .SetDescription("List the identifiers of all the query/retrieve operations on DICOM modalities, " + "as initiated by calls to `/modalities/{id}/query`. The length of this list is bounded " + "by the `QueryRetrieveSize` configuration option of Orthanc. " + "https://book.orthanc-server.com/users/rest.html#performing-query-retrieve-c-find-and-find-with-rest") + .AddAnswerType(MimeType_Json, "JSON array containing the identifiers"); + return; + } + ServerContext& context = OrthancRestApi::GetContext(call); std::list queries; @@ -629,6 +642,23 @@ static void ListQueryAnswers(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("List answers to a query") + .SetDescription("List the indices of all the available answers resulting from a query/retrieve operation " + "on some DICOM modality, whose identifier is provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest") + .SetHttpGetArgument("expand", RestApiCallDocumentation::Type_String, + "If present, retrieve detailed information about the individual answers", false) + .SetHttpGetArgument("simplify", RestApiCallDocumentation::Type_String, + "If present and if `expand` is present, format the tags of the answers in human-readable format", false) + .AddAnswerType(MimeType_Json, "JSON array containing the indices of the answers, or detailed information " + "about the reported answers (if `expand` argument is provided)"); + return; + } + const bool expand = call.HasArgument("expand"); const bool simplify = call.HasArgument("simplify"); @@ -661,6 +691,21 @@ static void GetQueryOneAnswer(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("Get one answer") + .SetDescription("Get the content (DICOM tags) of one answer associated with the " + "query/retrieve operation whose identifier is provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest") + .SetUriArgument("index", "Index of the answer") + .SetHttpGetArgument("simplify", RestApiCallDocumentation::Type_String, + "If present, format the tags of the answer in human-readable format", false) + .AddAnswerType(MimeType_Json, "JSON object containing the DICOM tags of the answer"); + return; + } + size_t index = boost::lexical_cast(call.GetUriComponent("index", "")); QueryAccessor query(call); @@ -734,10 +779,37 @@ OrthancRestApi::GetApi(call).SubmitCommandsJob (call, job.release(), true /* synchronous by default */, body); } + + + static void DocumentRetrieveShared(RestApiPostCall& call) + { + OrthancRestApi::DocumentSubmitCommandsJob(call); + call.GetDocumentation() + .SetTag("Networking") + .SetUriArgument("id", "Identifier of the query of interest") + .SetRequestField(KEY_TARGET_AET, RestApiCallDocumentation::Type_String, + "AET of the target modality. By default, the AET of Orthanc is used, as defined in the " + "`DicomAet` configuration option.", false) + .SetRequestField(KEY_TIMEOUT, RestApiCallDocumentation::Type_Number, + "Timeout for the C-MOVE command, in seconds.", false) + .AddRequestType(MimeType_PlainText, "AET of the target modality"); + } static void RetrieveOneAnswer(RestApiPostCall& call) { + if (call.IsDocumentation()) + { + DocumentRetrieveShared(call); + call.GetDocumentation() + .SetSummary("Retrieve one answer") + .SetDescription("Start a C-MOVE command as a job, in order to retrieve one answer associated with the " + "query/retrieve operation whose identifiers are provided in the URL: " + "https://book.orthanc-server.com/users/rest.html#performing-retrieve-c-move") + .SetUriArgument("index", "Index of the answer"); + return; + } + size_t index = boost::lexical_cast(call.GetUriComponent("index", "")); SubmitRetrieveJob(call, false, index); } @@ -745,12 +817,37 @@ static void RetrieveAllAnswers(RestApiPostCall& call) { + if (call.IsDocumentation()) + { + DocumentRetrieveShared(call); + call.GetDocumentation() + .SetSummary("Retrieve all answers") + .SetDescription("Start a C-MOVE command as a job, in order to retrieve all the answers associated with the " + "query/retrieve operation whose identifier is provided in the URL: " + "https://book.orthanc-server.com/users/rest.html#performing-retrieve-c-move"); + return; + } + SubmitRetrieveJob(call, true, 0); } static void GetQueryArguments(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("Get original query arguments") + .SetDescription("Get the original DICOM filter associated with the query/retrieve operation " + "whose identifier is provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest") + .SetHttpGetArgument("simplify", RestApiCallDocumentation::Type_String, + "If present, format the tags of the DICOM filter in human-readable format", false) + .AddAnswerType(MimeType_Json, "Content of the original query"); + return; + } + QueryAccessor query(call); AnswerDicomMap(call, query.GetHandler().GetQuery(), call.HasArgument("simplify")); } @@ -758,6 +855,18 @@ static void GetQueryLevel(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("Get level of original query") + .SetDescription("Get the query level (value of the `QueryRetrieveLevel` tag) of the query/retrieve operation " + "whose identifier is provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest") + .AddAnswerType(MimeType_PlainText, "The level"); + return; + } + QueryAccessor query(call); call.GetOutput().AnswerBuffer(EnumerationToString(query.GetHandler().GetLevel()), MimeType_PlainText); } @@ -765,6 +874,18 @@ static void GetQueryModality(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("Get modality of original query") + .SetDescription("Get the identifier of the DICOM modality that was targeted by the query/retrieve operation " + "whose identifier is provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest") + .AddAnswerType(MimeType_PlainText, "The identifier of the DICOM modality"); + return; + } + QueryAccessor query(call); call.GetOutput().AnswerBuffer(query.GetHandler().GetModalitySymbolicName(), MimeType_PlainText); } @@ -772,6 +893,16 @@ static void DeleteQuery(RestApiDeleteCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("Delete a query") + .SetDescription("Delete the query/retrieve operation whose identifier is provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest"); + return; + } + ServerContext& context = OrthancRestApi::GetContext(call); context.GetQueryRetrieveArchive().Remove(call.GetUriComponent("id", "")); call.GetOutput().AnswerBuffer("", MimeType_PlainText); @@ -780,6 +911,17 @@ static void ListQueryOperations(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("List operations on a query") + .SetDescription("List the available operations for the query/retrieve operation whose identifier is provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest") + .AddAnswerType(MimeType_Json, "JSON array containing the list of operations"); + return; + } + // Ensure that the query of interest does exist QueryAccessor query(call); @@ -789,6 +931,19 @@ static void ListQueryAnswerOperations(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("List operations on an answer") + .SetDescription("List the available operations on an answer associated with the " + "query/retrieve operation whose identifier is provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest") + .SetUriArgument("index", "Index of the answer") + .AddAnswerType(MimeType_Json, "JSON array containing the list of operations"); + return; + } + // Ensure that the query of interest does exist QueryAccessor query(call); @@ -830,6 +985,25 @@ CHILDREN_LEVEL == ResourceType_Series || CHILDREN_LEVEL == ResourceType_Instance); + if (call.IsDocumentation()) + { + const std::string resources = GetResourceTypeText(CHILDREN_LEVEL, true /* plural */, false /* lower case */); + call.GetDocumentation() + .SetTag("Networking") + .SetSummary("Query the child " + resources + " of an answer") + .SetDescription("Issue a second DICOM C-FIND operation, in order to query the child " + resources + + " associated with one answer to some query/retrieve operation whose identifiers are provided in the URL") + .SetUriArgument("id", "Identifier of the query of interest") + .SetUriArgument("index", "Index of the answer") + .SetRequestField(KEY_QUERY, RestApiCallDocumentation::Type_JsonObject, + "Filter on the DICOM tags", false) + .SetAnswerField("ID", RestApiCallDocumentation::Type_JsonObject, + "Identifier of the query, to be used with `/queries/{id}`") + .SetAnswerField("Path", RestApiCallDocumentation::Type_JsonObject, + "Root path to the query in the REST API"); + return; + } + ServerContext& context = OrthancRestApi::GetContext(call); std::unique_ptr handler(new QueryRetrieveHandler(context)); @@ -1205,7 +1379,7 @@ .SetTag("Networking") .SetSummary("List operations on peer") .SetDescription("List the operations that are available for an Orthanc peer.") - .SetUriArgument("id", "Orthanc identifier of the peer of interest") + .SetUriArgument("id", "Identifier of the peer of interest") .AddAnswerType(MimeType_Json, "List of the available operations"); return; } @@ -1275,7 +1449,7 @@ .SetSummary("Get peer system information") .SetDescription("Get system information about some Orthanc peer. This corresponds to doing a `GET` request " "against the `/system` URI of the remote peer. This route can be used to test connectivity.") - .SetUriArgument("id", "Orthanc identifier of the peer of interest") + .SetUriArgument("id", "Identifier of the peer of interest") .AddAnswerType(MimeType_Json, "System information about the peer"); return; } @@ -1322,7 +1496,7 @@ .SetTag("Networking") .SetSummary("Get peer configuration") .SetDescription("Get detailed information about the configuration of some Orthanc peer.") - .SetUriArgument("id", "Orthanc identifier of the peer of interest") + .SetUriArgument("id", "Identifier of the peer of interest") .AddAnswerType(MimeType_Json, "Configuration of the peer") .SetSample(sample); return; @@ -1410,7 +1584,7 @@ .SetTag("Networking") .SetSummary("List operations on modality") .SetDescription("List the operations that are available for a DICOM modality.") - .SetUriArgument("id", "Orthanc identifier of the DICOM modality of interest") + .SetUriArgument("id", "Identifier of the DICOM modality of interest") .AddAnswerType(MimeType_Json, "List of the available operations"); return; } @@ -1463,7 +1637,7 @@ .SetSummary("Delete DICOM modality") .SetDescription("Delete one DICOM modality. This change is permanent iff. `DicomModalitiesInDatabase` is `true`, " "otherwise it is lost at the next restart of Orthanc.") - .SetUriArgument("id", "Orthanc identifier of the DICOM modality of interest"); + .SetUriArgument("id", "Identifier of the DICOM modality of interest"); return; } @@ -1501,7 +1675,7 @@ .SetTag("Networking") .SetSummary("Get modality configuration") .SetDescription("Get detailed information about the configuration of some DICOM modality.") - .SetUriArgument("id", "Orthanc identifier of the modality of interest") + .SetUriArgument("id", "Identifier of the modality of interest") .AddAnswerType(MimeType_Json, "Configuration of the modality") .SetSample(sample); return; @@ -1551,7 +1725,7 @@ .SetSummary("Delete Orthanc peer") .SetDescription("Delete one Orthanc peer. This change is permanent iff. `OrthancPeersInDatabase` is `true`, " "otherwise it is lost at the next restart of Orthanc.") - .SetUriArgument("id", "Orthanc identifier of the Orthanc peer of interest"); + .SetUriArgument("id", "Identifier of the Orthanc peer of interest"); return; } diff -r 9d6fa3da8f00 -r cd96c807ca3d OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue Dec 29 10:24:17 2020 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue Dec 29 11:53:42 2020 +0100 @@ -2221,6 +2221,39 @@ enum DicomModule module> static void GetModule(RestApiGetCall& call) { + if (call.IsDocumentation()) + { + const std::string resource = GetResourceTypeText(resourceType, false /* plural */, false /* lower case */); + std::string m; + switch (module) + { + case DicomModule_Patient: + m = "patient"; + break; + case DicomModule_Study: + m = "study"; + break; + case DicomModule_Series: + m = "series"; + break; + case DicomModule_Instance: + m = "instance"; + break; + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + call.GetDocumentation() + .SetTag(GetResourceTypeText(resourceType, true /* plural */, true /* upper case */)) + .SetSummary("Get " + m + " module" + std::string(resource == m ? "" : " of " + resource)) + .SetDescription("Get the " + m + " module of the DICOM " + resource + " whose Orthanc identifier is provided in the URL") + .SetUriArgument("id", "Orthanc identifier of the " + resource + " of interest") + .SetHttpGetArgument("ignore-length", RestApiCallDocumentation::Type_JsonListOfStrings, + "Also include the DICOM tags that are provided in this list, even if their associated value is long", false) + .AddAnswerType(MimeType_Json, "Information about the DICOM " + resource) + .SetHttpGetSample(GetDocumentationSampleResource(resourceType) + "/" + (*call.GetFullUri().rbegin()), true); + return; + } + GetModuleInternal(call, resourceType, module); }