changeset 6108:21370b265a86 attach-custom-data

SDK: Get & Update Attachment CustomData
author Alain Mazy <am@orthanc.team>
date Wed, 14 May 2025 13:11:38 +0200
parents d71be7893e49
children 370479295564
files OrthancServer/CMakeLists.txt OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/OrthancPlugins.h OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Sources/Database/IDatabaseWrapper.h OrthancServer/Sources/Database/InstallKeyValueStore.sql OrthancServer/Sources/Database/InstallKeyValueStoresAndQueues.sql OrthancServer/Sources/Database/PrepareDatabase.sql OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h
diffstat 14 files changed, 309 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/CMakeLists.txt	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/CMakeLists.txt	Wed May 14 13:11:38 2025 +0200
@@ -253,7 +253,7 @@
   INSTALL_TRACK_ATTACHMENTS_SIZE    ${CMAKE_SOURCE_DIR}/Sources/Database/InstallTrackAttachmentsSize.sql
   INSTALL_LABELS_TABLE              ${CMAKE_SOURCE_DIR}/Sources/Database/InstallLabelsTable.sql
   INSTALL_REVISION_AND_CUSTOM_DATA  ${CMAKE_SOURCE_DIR}/Sources/Database/InstallRevisionAndCustomData.sql  
-  INSTALL_KEY_VALUE_STORE           ${CMAKE_SOURCE_DIR}/Sources/Database/InstallKeyValueStore.sql
+  INSTALL_KEY_VALUE_STORE_AND_QUEUES ${CMAKE_SOURCE_DIR}/Sources/Database/InstallKeyValueStoresAndQueues.sql
   )
 
 if (STANDALONE_BUILD)
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Wed May 14 13:11:38 2025 +0200
@@ -1484,6 +1484,18 @@
       throw OrthancException(ErrorCode_InternalError);  // Not supported
     }
 
+    virtual bool GetAttachment(FileInfo& attachment,
+                               int64_t& revision,
+                               const std::string& attachmentUuid) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);  // Not supported
+    }
+
+    virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
+                                            const std::string& customData) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);  // Not supported
+    }
   };
 
 
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp	Wed May 14 13:11:38 2025 +0200
@@ -679,7 +679,6 @@
       }
     }
 
-    
     virtual bool LookupGlobalProperty(std::string& target,
                                       GlobalProperty property,
                                       bool shared) ORTHANC_OVERRIDE
@@ -1097,6 +1096,20 @@
       throw OrthancException(ErrorCode_InternalError);  // Not supported
     }
 
+    virtual bool GetAttachment(FileInfo& attachment,
+                               int64_t& revision,
+                               const std::string& attachmentUuid) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);  // Not supported
+    }
+
+    virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
+                                            const std::string& customData) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);  // Not supported
+    }
+
+
   };
 
   
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Wed May 14 13:11:38 2025 +0200
@@ -1018,7 +1018,21 @@
       }
     }
 
-    
+
+    virtual bool GetAttachment(FileInfo& attachment,
+                               int64_t& revision,
+                               const std::string& attachmentUuid) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);  // TODO_ATTACH_CUSTOM_DATA
+    }
+
+    virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
+                                            const std::string& customData) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);  // TODO_ATTACH_CUSTOM_DATA
+    }
+
+
     virtual bool LookupGlobalProperty(std::string& target,
                                       GlobalProperty property,
                                       bool shared) ORTHANC_OVERRIDE
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Wed May 14 13:11:38 2025 +0200
@@ -4681,6 +4681,31 @@
     }
   }
 
+  void OrthancPlugins::ApplyGetAttachmentCustomData(const _OrthancPluginGetAttachmentCustomData& parameters)
+  {
+    PImpl::ServerContextReference lock(*pimpl_);
+    FileInfo fileInfo;
+    int64_t revision;
+    
+    if (lock.GetContext().GetIndex().GetAttachment(fileInfo, revision, parameters.attachmentUuid))
+    {
+      CopyToMemoryBuffer(*parameters.customData, fileInfo.GetCustomData().size() > 0 ? fileInfo.GetCustomData().c_str() : NULL, fileInfo.GetCustomData().size());
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_UnknownResource);
+    }
+  }
+
+  void OrthancPlugins::ApplyUpdateAttachmentCustomData(const _OrthancPluginUpdateAttachmentCustomData& parameters)
+  {
+    PImpl::ServerContextReference lock(*pimpl_);
+    FileInfo fileInfo;
+    std::string customData(parameters.customData, parameters.customDataSize);
+    
+    lock.GetContext().GetIndex().UpdateAttachmentCustomData(parameters.attachmentUuid, customData);
+  }
+
   bool OrthancPlugins::HasKeyValueStore()
   {
     PImpl::ServerContextReference lock(*pimpl_);
@@ -5828,6 +5853,22 @@
         return true;
       }
 
+      case _OrthancPluginService_GetAttachmentCustomData:
+      {
+        const _OrthancPluginGetAttachmentCustomData& p =
+          *reinterpret_cast<const _OrthancPluginGetAttachmentCustomData*>(parameters);
+        ApplyGetAttachmentCustomData(p);
+        return true;
+      }
+
+      case _OrthancPluginService_UpdateAttachmentCustomData:
+      {
+        const _OrthancPluginUpdateAttachmentCustomData& p =
+          *reinterpret_cast<const _OrthancPluginUpdateAttachmentCustomData*>(parameters);
+        ApplyUpdateAttachmentCustomData(p);
+        return true;
+      }
+
       case _OrthancPluginService_StoreKeyValue:
       {
         if (!HasKeyValueStore())
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.h	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h	Wed May 14 13:11:38 2025 +0200
@@ -225,6 +225,10 @@
 
     void ApplyAdoptAttachment(const _OrthancPluginAdoptAttachment& parameters);
 
+    void ApplyGetAttachmentCustomData(const _OrthancPluginGetAttachmentCustomData& parameters);
+
+    void ApplyUpdateAttachmentCustomData(const _OrthancPluginUpdateAttachmentCustomData& parameters);
+
     bool HasKeyValueStore();
 
     void ApplyStoreKeyValue(const _OrthancPluginStoreKeyValue& parameters);
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Wed May 14 13:11:38 2025 +0200
@@ -475,6 +475,8 @@
     _OrthancPluginService_GetKeyValue = 49,                         /* New in Orthanc 1.12.99 */
     _OrthancPluginService_EnqueueValue = 50,                        /* New in Orthanc 1.12.99 */
     _OrthancPluginService_DequeueValue = 51,                        /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_GetAttachmentCustomData = 52,             /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_UpdateAttachmentCustomData = 53,          /* New in Orthanc 1.12.99 */
 
 
     /* Registration of callbacks */
@@ -9839,6 +9841,64 @@
 
   typedef struct
   {
+    const char*                   attachmentUuid; /* in */
+    // OrthancPluginContentType      contentType; /* in */
+    OrthancPluginMemoryBuffer*    customData;  /* out */
+  } _OrthancPluginGetAttachmentCustomData;
+
+  /**
+   * @brief Retrieve attachment customData from the Orthanc DB.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+TODO_ATTACH_CUSTOM_DATA TODO TODO
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginGetAttachmentCustomData(
+    OrthancPluginContext*         context,
+    const char*                   attachmentUuid, /* in */
+    // OrthancPluginContentType      contentType, /* in */
+    OrthancPluginMemoryBuffer*    customData /* out */
+  ) 
+  {
+    _OrthancPluginGetAttachmentCustomData params;
+    params.attachmentUuid = attachmentUuid;
+    // params.contentType = contentType;
+    params.customData = customData;
+
+    return context->InvokeService(context, _OrthancPluginService_GetAttachmentCustomData, &params);
+  }
+
+  typedef struct
+  {
+    const char*                   attachmentUuid; /* in */
+    const char*                   customData;  /* in */
+    int64_t                       customDataSize; /* in */
+  } _OrthancPluginUpdateAttachmentCustomData;
+
+
+  /**
+   * @brief Update attachment custom data in the Orthanc DB.  E.g if a plugin has moved an attachment.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+TODO_ATTACH_CUSTOM_DATA TODO TODO
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginUpdateAttachmentCustomData(
+    OrthancPluginContext*         context,
+    const char*                   attachmentUuid, /* in */
+    const char*                   customData,  /* in */
+    int64_t                       customDataSize /* in */
+  ) 
+  {
+    _OrthancPluginUpdateAttachmentCustomData params;
+    params.attachmentUuid = attachmentUuid;
+    params.customData = customData;
+    params.customDataSize = customDataSize;
+
+    return context->InvokeService(context, _OrthancPluginService_UpdateAttachmentCustomData, &params);
+  }
+
+
+  typedef struct
+  {
     const char*                   storeId;
     const char*                   key;
     const char*                   value;
--- a/OrthancServer/Sources/Database/IDatabaseWrapper.h	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h	Wed May 14 13:11:38 2025 +0200
@@ -286,6 +286,13 @@
                                     int64_t id,
                                     FileContentType contentType) = 0;
 
+      virtual bool GetAttachment(FileInfo& attachment,
+                                 int64_t& revision,
+                                 const std::string& attachmentUuid) = 0;
+
+      virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
+                                              const std::string& customData) = 0;
+
       /**
        * If "shared" is "true", the property is shared by all the
        * Orthanc servers that access the same database. If "shared" is
--- a/OrthancServer/Sources/Database/InstallKeyValueStore.sql	Tue May 13 14:10:30 2025 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
--- Orthanc - A Lightweight, RESTful DICOM Store
--- Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
--- Department, University Hospital of Liege, Belgium
--- Copyright (C) 2017-2023 Osimis S.A., Belgium
--- Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
--- Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, 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.
---
--- 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/>.
-
-
-CREATE TABLE KeyValueStores(
-       storeId TEXT NOT NULL,
-       key TEXT NOT NULL,
-       value TEXT NOT NULL,
-       PRIMARY KEY(storeId, key)  -- Prevents duplicates
-       );
-
-CREATE INDEX KeyValueStoresIndex ON KeyValueStores (storeId, key);
-
-CREATE TABLE Queues (
-       id INTEGER PRIMARY KEY AUTOINCREMENT,
-       queueId TEXT NOT NULL,
-       value TEXT
-);
-
-CREATE INDEX QueuesIndex ON Queues (queueId, id);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Sources/Database/InstallKeyValueStoresAndQueues.sql	Wed May 14 13:11:38 2025 +0200
@@ -0,0 +1,37 @@
+-- Orthanc - A Lightweight, RESTful DICOM Store
+-- Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+-- Department, University Hospital of Liege, Belgium
+-- Copyright (C) 2017-2023 Osimis S.A., Belgium
+-- Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
+-- Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, 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.
+--
+-- 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/>.
+
+
+CREATE TABLE KeyValueStores(
+       storeId TEXT NOT NULL,
+       key TEXT NOT NULL,
+       value TEXT NOT NULL,
+       PRIMARY KEY(storeId, key)  -- Prevents duplicates
+       );
+
+CREATE INDEX KeyValueStoresIndex ON KeyValueStores (storeId, key);
+
+CREATE TABLE Queues (
+       id INTEGER PRIMARY KEY AUTOINCREMENT,
+       queueId TEXT NOT NULL,
+       value TEXT
+);
+
+CREATE INDEX QueuesIndex ON Queues (queueId, id);
--- a/OrthancServer/Sources/Database/PrepareDatabase.sql	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Sources/Database/PrepareDatabase.sql	Wed May 14 13:11:38 2025 +0200
@@ -168,7 +168,7 @@
        );
 
 -- new in Orthanc 1.12.99
-CREATE INDEX KeyValueStoresIndex ON KeyValueStore (storeId, key);
+CREATE INDEX KeyValueStoresIndex ON KeyValueStores (storeId, key);
 
 -- new in Orthanc 1.12.99
 CREATE TABLE Queues (
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Wed May 14 13:11:38 2025 +0200
@@ -1784,6 +1784,43 @@
       }
     }
 
+    virtual bool GetAttachment(FileInfo& attachment,
+                               int64_t& revision,
+                               const std::string& attachmentUuid) ORTHANC_OVERRIDE
+    {
+      SQLite::Statement s(db_, SQLITE_FROM_HERE, 
+                          "SELECT uuid, uncompressedSize, compressionType, compressedSize, "
+                          "uncompressedMD5, compressedMD5, revision, customData, fileType FROM AttachedFiles WHERE uuid=?");
+      s.BindString(0, attachmentUuid);
+
+      if (!s.Step())
+      {
+        return false;
+      }
+      else
+      {
+        attachment = FileInfo(s.ColumnString(0),
+                              static_cast<FileContentType>(s.ColumnInt(8)),
+                              s.ColumnInt64(1),
+                              s.ColumnString(4),
+                              static_cast<CompressionType>(s.ColumnInt(2)),
+                              s.ColumnInt64(3),
+                              s.ColumnString(5),
+                              s.ColumnString(7));
+        revision = s.ColumnInt(6);
+        return true;
+      }
+    }
+
+    virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
+                                            const std::string& customData) ORTHANC_OVERRIDE
+    {
+      SQLite::Statement s(db_, SQLITE_FROM_HERE, 
+                          "UPDATE AttachedFiles SET customData=? WHERE uuid=?");
+      s.BindString(0, customData);
+      s.BindString(1, attachmentUuid);
+      s.Run();
+    }
 
     virtual bool LookupGlobalProperty(std::string& target,
                                       GlobalProperty property,
@@ -2521,11 +2558,11 @@
           db_.Execute(query);
         }
 
-        if (!db_.DoesTableExist("KeyValueStore"))
+        if (!db_.DoesTableExist("KeyValueStores"))
         {
-          LOG(INFO) << "Installing the \"KeyValueStore\" table";
+          LOG(INFO) << "Installing the \"KeyValueStores\" and \"Queues\" tables";
           std::string query;
-          ServerResources::GetFileResource(query, ServerResources::INSTALL_KEY_VALUE_STORE);
+          ServerResources::GetFileResource(query, ServerResources::INSTALL_KEY_VALUE_STORE_AND_QUEUES);
           db_.Execute(query);
         }
       }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Wed May 14 13:11:38 2025 +0200
@@ -3488,4 +3488,61 @@
     return operations.HasFound();
   }
 
+  bool StatelessDatabaseOperations::GetAttachment(FileInfo& attachment,
+                                                  int64_t& revision,
+                                                  const std::string& attachmentUuid)
+  {
+    class Operations : public ReadOnlyOperationsT3<FileInfo&, int64_t&, const std::string& >
+    {
+      bool found_;
+    public:
+      Operations():
+        found_(false)
+      {}
+
+      bool HasFound()
+      {
+        return found_;
+      }
+
+      virtual void ApplyTuple(ReadOnlyTransaction& transaction,
+                              const Tuple& tuple) ORTHANC_OVERRIDE
+      {
+        found_ = transaction.GetAttachment(tuple.get<0>(), tuple.get<1>(), tuple.get<2>());
+      }
+    };
+
+    Operations operations;
+    operations.Apply(*this, attachment, revision, attachmentUuid);
+
+    return operations.HasFound();
+  }
+
+  void StatelessDatabaseOperations::UpdateAttachmentCustomData(const std::string& attachmentUuid,
+                                                               const std::string& customData)
+  {
+    class Operations : public IReadWriteOperations
+    {
+    private:
+      const std::string& attachmentUuid_;
+      const std::string& customData_;
+
+    public:
+      Operations(const std::string& attachmentUuid,
+                 const std::string& customData) :
+        attachmentUuid_(attachmentUuid),
+        customData_(customData)
+      {
+      }
+
+      virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE
+      {
+        transaction.UpdateAttachmentCustomData(attachmentUuid_, customData_);
+      }
+    };
+
+    Operations operations(attachmentUuid, customData);
+    Apply(operations);
+  }
+
 }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Tue May 13 14:10:30 2025 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Wed May 14 13:11:38 2025 +0200
@@ -226,6 +226,13 @@
         return transaction_.LookupAttachment(attachment, revision, id, contentType);
       }
       
+      bool GetAttachment(FileInfo& attachment,
+                         int64_t& revision,
+                         const std::string& attachmentUuid)
+      {
+        return transaction_.GetAttachment(attachment, revision, attachmentUuid);
+      }
+
       bool LookupGlobalProperty(std::string& target,
                                 GlobalProperty property,
                                 bool shared)
@@ -464,6 +471,12 @@
       }
 
 
+      void UpdateAttachmentCustomData(const std::string& attachmentUuid,
+                                      const std::string& customData)
+      {
+        return transaction_.UpdateAttachmentCustomData(attachmentUuid, customData);
+      }
+
     };
 
 
@@ -559,6 +572,13 @@
                              /* out */ uint64_t& countSeries, 
                              /* out */ uint64_t& countInstances);
 
+    bool GetAttachment(FileInfo& attachment,
+                       int64_t& revision,
+                       const std::string& attachmentUuid);
+
+    void UpdateAttachmentCustomData(const std::string& attachmentUuid,
+                                    const std::string& customData);
+
     bool LookupAttachment(FileInfo& attachment,
                           int64_t& revision,
                           ResourceType level,