diff OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents OrthancServer/OrthancRestApi/OrthancRestArchive.cpp@6ddad3e0b569
children 05b8fd21089c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp	Wed Jun 10 20:30:34 2020 +0200
@@ -0,0 +1,334 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeadersServer.h"
+#include "OrthancRestApi.h"
+
+#include "../../Core/HttpServer/FilesystemHttpSender.h"
+#include "../../Core/OrthancException.h"
+#include "../../Core/SerializationToolbox.h"
+#include "../OrthancConfiguration.h"
+#include "../ServerContext.h"
+#include "../ServerJobs/ArchiveJob.h"
+
+
+namespace Orthanc
+{
+  static const char* const KEY_RESOURCES = "Resources";
+  static const char* const KEY_EXTENDED = "Extended";
+  static const char* const KEY_TRANSCODE = "Transcode";
+  
+  static void AddResourcesOfInterestFromArray(ArchiveJob& job,
+                                              const Json::Value& resources)
+  {
+    if (resources.type() != Json::arrayValue)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat,
+                             "Expected a list of strings (Orthanc identifiers)");
+    }
+    
+    for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++)
+    {
+      if (resources[i].type() != Json::stringValue)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat,
+                               "Expected a list of strings (Orthanc identifiers)");
+      }
+      else
+      {
+        job.AddResource(resources[i].asString());
+      }
+    }
+  }
+
+  
+  static void AddResourcesOfInterest(ArchiveJob& job         /* inout */,
+                                     const Json::Value& body /* in */)
+  {
+    if (body.type() == Json::arrayValue)
+    {
+      AddResourcesOfInterestFromArray(job, body);
+    }
+    else if (body.type() == Json::objectValue)
+    {
+      if (body.isMember(KEY_RESOURCES))
+      {
+        AddResourcesOfInterestFromArray(job, body[KEY_RESOURCES]);
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadFileFormat,
+                               "Missing field " + std::string(KEY_RESOURCES) +
+                               " in the JSON body");
+      }
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  static DicomTransferSyntax GetTransferSyntax(const std::string& value)
+  {
+    DicomTransferSyntax syntax;
+    if (LookupTransferSyntax(syntax, value))
+    {
+      return syntax;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange,
+                             "Unknown transfer syntax: " + value);
+    }
+  }
+  
+  
+  static void GetJobParameters(bool& synchronous,            /* out */
+                               bool& extended,               /* out */
+                               bool& transcode,              /* out */
+                               DicomTransferSyntax& syntax,  /* out */
+                               int& priority,                /* out */
+                               const Json::Value& body,      /* in */
+                               const bool defaultExtended    /* in */)
+  {
+    synchronous = OrthancRestApi::IsSynchronousJobRequest
+      (true /* synchronous by default */, body);
+
+    priority = OrthancRestApi::GetJobRequestPriority(body);
+
+    if (body.type() == Json::objectValue &&
+        body.isMember(KEY_EXTENDED))
+    {
+      extended = SerializationToolbox::ReadBoolean(body, KEY_EXTENDED);
+    }
+    else
+    {
+      extended = defaultExtended;
+    }
+
+    if (body.type() == Json::objectValue &&
+        body.isMember(KEY_TRANSCODE))
+    {
+      transcode = true;
+      syntax = GetTransferSyntax(SerializationToolbox::ReadString(body, KEY_TRANSCODE));
+    }
+    else
+    {
+      transcode = false;
+    }
+  }
+
+
+  static void SubmitJob(RestApiOutput& output,
+                        ServerContext& context,
+                        std::unique_ptr<ArchiveJob>& job,
+                        int priority,
+                        bool synchronous,
+                        const std::string& filename)
+  {
+    if (job.get() == NULL)
+    {
+      throw OrthancException(ErrorCode_NullPointer);
+    }
+
+    job->SetDescription("REST API");
+
+    if (synchronous)
+    {
+      boost::shared_ptr<TemporaryFile> tmp;
+
+      {
+        OrthancConfiguration::ReaderLock lock;
+        tmp.reset(lock.GetConfiguration().CreateTemporaryFile());
+      }
+
+      job->SetSynchronousTarget(tmp);
+    
+      Json::Value publicContent;
+      context.GetJobsEngine().GetRegistry().SubmitAndWait
+        (publicContent, job.release(), priority);
+      
+      {
+        // The archive is now created: Prepare the sending of the ZIP file
+        FilesystemHttpSender sender(tmp->GetPath(), MimeType_Zip);
+        sender.SetContentFilename(filename);
+
+        // Send the ZIP
+        output.AnswerStream(sender);
+      }
+    }
+    else
+    {
+      OrthancRestApi::SubmitGenericJob(output, context, job.release(), false, priority);
+    }
+  }
+
+  
+  template <bool IS_MEDIA,
+            bool DEFAULT_IS_EXTENDED  /* only makes sense for media (i.e. not ZIP archives) */ >
+  static void CreateBatch(RestApiPostCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    Json::Value body;
+    if (call.ParseJsonRequest(body))
+    {
+      bool synchronous, extended, transcode;
+      DicomTransferSyntax transferSyntax;
+      int priority;
+      GetJobParameters(synchronous, extended, transcode, transferSyntax,
+                       priority, body, DEFAULT_IS_EXTENDED);
+      
+      std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended));
+      AddResourcesOfInterest(*job, body);
+
+      if (transcode)
+      {
+        job->SetTranscode(transferSyntax);
+      }
+      
+      SubmitJob(call.GetOutput(), context, job, priority, synchronous, "Archive.zip");
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadFileFormat,
+                             "Expected a list of resources to archive in the body");
+    }
+  }
+  
+
+  template <bool IS_MEDIA,
+            bool DEFAULT_IS_EXTENDED  /* only makes sense for media (i.e. not ZIP archives) */ >
+  static void CreateSingleGet(RestApiGetCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    std::string id = call.GetUriComponent("id", "");
+
+    bool extended;
+    if (IS_MEDIA)
+    {
+      extended = call.HasArgument("extended");
+    }
+    else
+    {
+      extended = false;
+    }
+
+    std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended));
+    job->AddResource(id);
+
+    static const char* const TRANSCODE = "transcode";
+    if (call.HasArgument(TRANSCODE))
+    {
+      job->SetTranscode(GetTransferSyntax(call.GetArgument(TRANSCODE, "")));
+    }
+
+    SubmitJob(call.GetOutput(), context, job, 0 /* priority */,
+              true /* synchronous */, id + ".zip");
+  }
+
+
+  template <bool IS_MEDIA,
+            bool DEFAULT_IS_EXTENDED  /* only makes sense for media (i.e. not ZIP archives) */ >
+  static void CreateSinglePost(RestApiPostCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    std::string id = call.GetUriComponent("id", "");
+
+    Json::Value body;
+    if (call.ParseJsonRequest(body))
+    {
+      bool synchronous, extended, transcode;
+      DicomTransferSyntax transferSyntax;
+      int priority;
+      GetJobParameters(synchronous, extended, transcode, transferSyntax,
+                       priority, body, DEFAULT_IS_EXTENDED);
+      
+      std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended));
+      job->AddResource(id);
+
+      if (transcode)
+      {
+        job->SetTranscode(transferSyntax);
+      }
+
+      SubmitJob(call.GetOutput(), context, job, priority, synchronous, id + ".zip");
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+    
+  void OrthancRestApi::RegisterArchive()
+  {
+    Register("/patients/{id}/archive",
+             CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>);
+    Register("/studies/{id}/archive",
+             CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>);
+    Register("/series/{id}/archive",
+             CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>);
+
+    Register("/patients/{id}/archive",
+             CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>);
+    Register("/studies/{id}/archive",
+             CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>);
+    Register("/series/{id}/archive",
+             CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>);
+
+    Register("/patients/{id}/media",
+             CreateSingleGet<true /* media */, false /* not extended by default */>);
+    Register("/studies/{id}/media",
+             CreateSingleGet<true /* media */, false /* not extended by default */>);
+    Register("/series/{id}/media",
+             CreateSingleGet<true /* media */, false /* not extended by default */>);
+
+    Register("/patients/{id}/media",
+             CreateSinglePost<true /* media */, false /* not extended by default */>);
+    Register("/studies/{id}/media",
+             CreateSinglePost<true /* media */, false /* not extended by default */>);
+    Register("/series/{id}/media",
+             CreateSinglePost<true /* media */, false /* not extended by default */>);
+
+    Register("/tools/create-archive",
+             CreateBatch<false /* ZIP */,  false /* extended makes no sense in ZIP */>);
+    Register("/tools/create-media",
+             CreateBatch<true /* media */, false /* not extended by default */>);
+    Register("/tools/create-media-extended",
+             CreateBatch<true /* media */, true /* extended by default */>);
+  }
+}