# HG changeset patch # User Sebastien Jodogne # Date 1623857867 -7200 # Node ID da1edb7d6332a1ad68c8fa9e7e1a63876f54faf3 # Parent 45bce660ce3a5c0a12febfe31e69a9ce892e634a "/tools/bulk-delete" to delete a group of multiple, unrelated resources at once diff -r 45bce660ce3a -r da1edb7d6332 NEWS --- a/NEWS Wed Jun 16 16:44:04 2021 +0200 +++ b/NEWS Wed Jun 16 17:37:47 2021 +0200 @@ -12,8 +12,9 @@ * API version upgraded to 13 * New routes: - - "/tools/bulk-anonymize" to anonymize groups of multiple, unrelated resources at once - - "/tools/bulk-modify" to modify groups of multiple, unrelated resources at once + - "/tools/bulk-anonymize" to anonymize a group of multiple, unrelated resources at once + - "/tools/bulk-delete" to delete a group of multiple, unrelated resources at once + - "/tools/bulk-modify" to modify a group of multiple, unrelated resources at once * ZIP archive/media generated in synchronous mode are now streamed by default * "Replace" tags in "/modify" and "/anonymize" now supports value representation AT * "/jobs/..." has new field "ErrorDetails" to help identify the cause of an error diff -r 45bce660ce3a -r da1edb7d6332 OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed Jun 16 16:44:04 2021 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed Jun 16 17:37:47 2021 +0200 @@ -1973,7 +1973,7 @@ } - bool StatelessDatabaseOperations::DeleteResource(Json::Value& target, + bool StatelessDatabaseOperations::DeleteResource(Json::Value& remainingAncestor, const std::string& uuid, ResourceType expectedType) { @@ -1981,16 +1981,16 @@ { private: bool found_; - Json::Value& target_; + Json::Value& remainingAncestor_; const std::string& uuid_; ResourceType expectedType_; public: - Operations(Json::Value& target, + Operations(Json::Value& remainingAncestor, const std::string& uuid, ResourceType expectedType) : found_(false), - target_(target), + remainingAncestor_(remainingAncestor), uuid_(uuid), expectedType_(expectedType) { @@ -2019,20 +2019,20 @@ ResourceType remainingLevel; if (transaction.GetTransactionContext().LookupRemainingLevel(remainingPublicId, remainingLevel)) { - target_["RemainingAncestor"] = Json::Value(Json::objectValue); - target_["RemainingAncestor"]["Path"] = GetBasePath(remainingLevel, remainingPublicId); - target_["RemainingAncestor"]["Type"] = EnumerationToString(remainingLevel); - target_["RemainingAncestor"]["ID"] = remainingPublicId; + remainingAncestor_["RemainingAncestor"] = Json::Value(Json::objectValue); + remainingAncestor_["RemainingAncestor"]["Path"] = GetBasePath(remainingLevel, remainingPublicId); + remainingAncestor_["RemainingAncestor"]["Type"] = EnumerationToString(remainingLevel); + remainingAncestor_["RemainingAncestor"]["ID"] = remainingPublicId; } else { - target_["RemainingAncestor"] = Json::nullValue; + remainingAncestor_["RemainingAncestor"] = Json::nullValue; } } } }; - Operations operations(target, uuid, expectedType); + Operations operations(remainingAncestor, uuid, expectedType); Apply(operations); return operations.IsFound(); } diff -r 45bce660ce3a -r da1edb7d6332 OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Wed Jun 16 16:44:04 2021 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Wed Jun 16 17:37:47 2021 +0200 @@ -565,7 +565,7 @@ ResourceType queryLevel, size_t limit); - bool DeleteResource(Json::Value& target /* out */, + bool DeleteResource(Json::Value& remainingAncestor /* out */, const std::string& uuid, ResourceType expectedType); diff -r 45bce660ce3a -r da1edb7d6332 OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp Wed Jun 16 16:44:04 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp Wed Jun 16 17:37:47 2021 +0200 @@ -425,7 +425,7 @@ DocumentModifyOptions(call); call.GetDocumentation() .SetTag("System") - .SetSummary("Modify a set of instances") + .SetSummary("Modify a set of resources") .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings, "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false) .SetDescription("Start a job that will modify all the DICOM patients, studies, series or instances " @@ -483,7 +483,7 @@ DocumentAnonymizationOptions(call); call.GetDocumentation() .SetTag("System") - .SetSummary("Anonymize a set of instances") + .SetSummary("Anonymize a set of resources") .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings, "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false) .SetDescription("Start a job that will anonymize all the DICOM patients, studies, series or instances " diff -r 45bce660ce3a -r da1edb7d6332 OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Wed Jun 16 16:44:04 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Wed Jun 16 17:37:47 2021 +0200 @@ -44,6 +44,7 @@ #include "../../../OrthancFramework/Sources/Images/ImageProcessing.h" #include "../../../OrthancFramework/Sources/Logging.h" #include "../../../OrthancFramework/Sources/MultiThreading/Semaphore.h" +#include "../../../OrthancFramework/Sources/SerializationToolbox.h" #include "../OrthancConfiguration.h" #include "../Search/DatabaseLookup.h" @@ -278,10 +279,10 @@ return; } - Json::Value result; - if (OrthancRestApi::GetContext(call).DeleteResource(result, call.GetUriComponent("id", ""), resourceType)) + Json::Value remainingAncestor; + if (OrthancRestApi::GetContext(call).DeleteResource(remainingAncestor, call.GetUriComponent("id", ""), resourceType)) { - call.GetOutput().AnswerJson(result); + call.GetOutput().AnswerJson(remainingAncestor); } } @@ -3103,6 +3104,52 @@ } + static void BulkDelete(RestApiPostCall& call) + { + if (call.IsDocumentation()) + { + call.GetDocumentation() + .SetTag("System") + .SetSummary("Delete a set of instances") + .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings, + "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false) + .SetDescription("Dellete all the DICOM patients, studies, series or instances " + "whose identifiers are provided in the `Resources` field."); + return; + } + + ServerContext& context = OrthancRestApi::GetContext(call); + + Json::Value request; + if (!call.ParseJsonRequest(request) || + request.type() != Json::objectValue) + { + throw OrthancException(ErrorCode_BadRequest, + "The body must contain a JSON object"); + } + else + { + std::set resources; + SerializationToolbox::ReadSetOfStrings(resources, request, "Resources"); + + for (std::set::const_iterator + it = resources.begin(); it != resources.end(); ++it) + { + ResourceType type; + Json::Value remainingAncestor; // Unused + + if (!context.GetIndex().LookupResourceType(type, *it) || + !context.DeleteResource(remainingAncestor, *it, type)) + { + CLOG(INFO, HTTP) << "Unknown resource during a bulk deletion: " << *it; + } + } + + call.GetOutput().AnswerBuffer("", MimeType_PlainText); + } + } + + void OrthancRestApi::RegisterResources() { Register("/instances", ListResources); @@ -3221,5 +3268,7 @@ Register("/series/{id}/reconstruct", ReconstructResource); Register("/instances/{id}/reconstruct", ReconstructResource); Register("/tools/reconstruct", ReconstructAllResources); + + Register("/tools/bulk-delete", BulkDelete); } } diff -r 45bce660ce3a -r da1edb7d6332 OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Wed Jun 16 16:44:04 2021 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Wed Jun 16 17:37:47 2021 +0200 @@ -1188,7 +1188,7 @@ } - bool ServerContext::DeleteResource(Json::Value& target, + bool ServerContext::DeleteResource(Json::Value& remainingAncestor, const std::string& uuid, ResourceType expectedType) { @@ -1199,7 +1199,7 @@ PublishDicomCacheMetrics(); } - return index_.DeleteResource(target, uuid, expectedType); + return index_.DeleteResource(remainingAncestor, uuid, expectedType); } diff -r 45bce660ce3a -r da1edb7d6332 OrthancServer/Sources/ServerContext.h --- a/OrthancServer/Sources/ServerContext.h Wed Jun 16 16:44:04 2021 +0200 +++ b/OrthancServer/Sources/ServerContext.h Wed Jun 16 17:37:47 2021 +0200 @@ -348,7 +348,7 @@ return jobsEngine_; } - bool DeleteResource(Json::Value& target, + bool DeleteResource(Json::Value& remainingAncestor, const std::string& uuid, ResourceType expectedType);