changeset 4694:da1edb7d6332

"/tools/bulk-delete" to delete a group of multiple, unrelated resources at once
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 16 Jun 2021 17:37:47 +0200
parents 45bce660ce3a
children 651f069c7f77
files NEWS OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerContext.h
diffstat 7 files changed, 71 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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();
   }
--- 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);
 
--- 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 "
--- 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<std::string> resources;
+      SerializationToolbox::ReadSetOfStrings(resources, request, "Resources");
+
+      for (std::set<std::string>::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<ResourceType_Instance>);
@@ -3221,5 +3268,7 @@
     Register("/series/{id}/reconstruct", ReconstructResource<ResourceType_Series>);
     Register("/instances/{id}/reconstruct", ReconstructResource<ResourceType_Instance>);
     Register("/tools/reconstruct", ReconstructAllResources);
+
+    Register("/tools/bulk-delete", BulkDelete);
   }
 }
--- 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);
   }
 
 
--- 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);