changeset 5757:5463c3ae3235 large-queries

refactored extended /changes
author Alain Mazy <am@orthanc.team>
date Thu, 05 Sep 2024 18:21:56 +0200
parents 3765085693e5
children ca06dde85358
files NEWS OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto OrthancServer/Sources/Database/BaseDatabaseWrapper.cpp OrthancServer/Sources/Database/BaseDatabaseWrapper.h OrthancServer/Sources/Database/IDatabaseWrapper.h OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/Sources/OrthancRestApi/OrthancRestChanges.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp
diffstat 11 files changed, 108 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Thu Jul 04 07:40:58 2024 +0200
+++ b/NEWS	Thu Sep 05 18:21:56 2024 +0200
@@ -6,12 +6,13 @@
 
 * Improved parsing of multiple numerical values in DICOM tags.
   https://discourse.orthanc-server.org/t/qido-includefield-with-sequences/4746/6
-* Added "ExtendedApiV1" if you have a DB that supports it (the default SQLite DB
-  or PostgreSQL vX.X, MySQL vX.X, ODBC vX.X).
-  - /extended-api-v1/changes now supports 2 more options: 'type' to filter
-    the changes returned by the query and 'to' to potentially cycle through
-    changes in reverse order.
-    example: /extended-api-v1/changes?type=StableStudy&to=7584&limit=100
+* in /system, added a new field "Capabilities" with new values:
+  - "HasExtendedChanges" for DB backend that provides this feature (the default SQLite DB
+    or PostgreSQL vX.X, MySQL vX.X, ODBC vX.X).
+* With DB backend with "HasExtendedChanges" support, /changes now supports 2 more options: 
+  - 'type' to filter the changes returned by the query 
+  - 'to' to potentially cycle through changes in reverse order.
+  example: /changes?type=StableStudy&to=7584&limit=100
 
 
 Maintenance
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Thu Sep 05 18:21:56 2024 +0200
@@ -495,22 +495,22 @@
       }
     }
 
-    virtual void GetChanges2(std::list<ServerIndexChange>& target /*out*/,
-                             bool& done /*out*/,
-                             int64_t since,
-                             int64_t to,
-                             uint32_t limit,
-                             ChangeType changeType) ORTHANC_OVERRIDE
+    virtual void GetChangesExtended(std::list<ServerIndexChange>& target /*out*/,
+                                    bool& done /*out*/,
+                                    int64_t since,
+                                    int64_t to,
+                                    uint32_t limit,
+                                    ChangeType changeType) ORTHANC_OVERRIDE
     {
-      assert(database_.GetDatabaseCapabilities().HasExtendedApiV1());
+      assert(database_.GetDatabaseCapabilities().HasExtendedChanges());
 
       DatabasePluginMessages::TransactionRequest request;
       DatabasePluginMessages::TransactionResponse response;
 
-      request.mutable_get_changes2()->set_since(since);
-      request.mutable_get_changes2()->set_limit(limit);
-      request.mutable_get_changes2()->set_to(to);
-      request.mutable_get_changes2()->set_change_type(changeType);
+      request.mutable_get_changes_extended()->set_since(since);
+      request.mutable_get_changes_extended()->set_limit(limit);
+      request.mutable_get_changes_extended()->set_to(to);
+      request.mutable_get_changes_extended()->set_change_type(changeType);
       ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHANGES_2, request);
 
       done = response.get_changes().done();
@@ -1391,7 +1391,7 @@
       dbCapabilities_.SetAtomicIncrementGlobalProperty(systemInfo.supports_increment_global_property());
       dbCapabilities_.SetUpdateAndGetStatistics(systemInfo.has_update_and_get_statistics());
       dbCapabilities_.SetMeasureLatency(systemInfo.has_measure_latency());
-      dbCapabilities_.SetHasExtendedApiV1(systemInfo.has_extended_api_v1());
+      dbCapabilities_.SetHasExtendedChanges(systemInfo.has_extended_changes());
     }
 
     open_ = true;
--- a/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto	Thu Sep 05 18:21:56 2024 +0200
@@ -141,7 +141,7 @@
     bool supports_increment_global_property = 5;
     bool has_update_and_get_statistics = 6;
     bool has_measure_latency = 7;
-    bool has_extended_api_v1 = 8;
+    bool has_extended_changes = 8;
   }
 }
 
@@ -415,7 +415,7 @@
   }
 }
 
-message GetChanges2 {
+message GetChangesExtended {
   message Request {
     int64 since = 1;
     int64 to = 2;
@@ -893,7 +893,7 @@
   ListLabels.Request                      list_labels = 147;
   IncrementGlobalProperty.Request         increment_global_property = 148;
   UpdateAndGetStatistics.Request          update_and_get_statistics = 149;
-  GetChanges2.Request                     get_changes2 = 150;
+  GetChangesExtended.Request              get_changes_extended = 150;
 }
 
 message TransactionResponse {
@@ -947,7 +947,7 @@
   ListLabels.Response                      list_labels = 147;
   IncrementGlobalProperty.Response         increment_global_property = 148;
   UpdateAndGetStatistics.Response          update_and_get_statistics = 149;
-  GetChanges2.Response                     get_changes2 = 150;
+  GetChangesExtended.Response              get_changes_extended = 150;
 }
 
 enum RequestType {
--- a/OrthancServer/Sources/Database/BaseDatabaseWrapper.cpp	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Sources/Database/BaseDatabaseWrapper.cpp	Thu Sep 05 18:21:56 2024 +0200
@@ -45,12 +45,12 @@
     throw OrthancException(ErrorCode_NotImplemented);  // Not supported
   }
 
-  void BaseDatabaseWrapper::BaseTransaction::GetChanges2(std::list<ServerIndexChange>& target /*out*/,
-                                                         bool& done /*out*/,
-                                                         int64_t since,
-                                                         int64_t to,
-                                                         uint32_t limit,
-                                                         ChangeType filterType)
+  void BaseDatabaseWrapper::BaseTransaction::GetChangesExtended(std::list<ServerIndexChange>& target /*out*/,
+                                                                bool& done /*out*/,
+                                                                int64_t since,
+                                                                int64_t to,
+                                                                uint32_t limit,
+                                                                ChangeType filterType)
   {
     throw OrthancException(ErrorCode_NotImplemented);  // Not supported
   }
--- a/OrthancServer/Sources/Database/BaseDatabaseWrapper.h	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Sources/Database/BaseDatabaseWrapper.h	Thu Sep 05 18:21:56 2024 +0200
@@ -48,12 +48,12 @@
                                           int64_t& compressedSize,
                                           int64_t& uncompressedSize) ORTHANC_OVERRIDE;
 
-      virtual void GetChanges2(std::list<ServerIndexChange>& target /*out*/,
-                               bool& done /*out*/,
-                               int64_t since,
-                               int64_t to,
-                               uint32_t limit,
-                               ChangeType filterType) ORTHANC_OVERRIDE;
+      virtual void GetChangesExtended(std::list<ServerIndexChange>& target /*out*/,
+                                      bool& done /*out*/,
+                                      int64_t since,
+                                      int64_t to,
+                                      uint32_t limit,
+                                      ChangeType filterType) ORTHANC_OVERRIDE;
 
     };
 
--- a/OrthancServer/Sources/Database/IDatabaseWrapper.h	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h	Thu Sep 05 18:21:56 2024 +0200
@@ -52,7 +52,7 @@
       bool hasAtomicIncrementGlobalProperty_;
       bool hasUpdateAndGetStatistics_;
       bool hasMeasureLatency_;
-      bool hasExtendedApiV1_;
+      bool hasExtendedChanges_;
 
     public:
       Capabilities() :
@@ -62,7 +62,7 @@
         hasAtomicIncrementGlobalProperty_(false),
         hasUpdateAndGetStatistics_(false),
         hasMeasureLatency_(false),
-        hasExtendedApiV1_(false)
+        hasExtendedChanges_(false)
       {
       }
 
@@ -96,14 +96,14 @@
         return hasLabelsSupport_;
       }
 
-      void SetHasExtendedApiV1(bool value)
+      void SetHasExtendedChanges(bool value)
       {
-        hasExtendedApiV1_ = value;
+        hasExtendedChanges_ = value;
       }
 
-      bool HasExtendedApiV1() const
+      bool HasExtendedChanges() const
       {
-        return hasExtendedApiV1_;
+        return hasExtendedChanges_;
       }
 
       void SetAtomicIncrementGlobalProperty(bool value)
@@ -366,12 +366,12 @@
                                           int64_t& uncompressedSize) = 0;
 
       // New in Orthanc 1.13.0
-      virtual void GetChanges2(std::list<ServerIndexChange>& target /*out*/,
-                               bool& done /*out*/,
-                               int64_t since,
-                               int64_t to,
-                               uint32_t limit,
-                               ChangeType filterType) = 0;
+      virtual void GetChangesExtended(std::list<ServerIndexChange>& target /*out*/,
+                                      bool& done /*out*/,
+                                      int64_t since,
+                                      int64_t to,
+                                      uint32_t limit,
+                                      ChangeType filterType) = 0;
 
     };
 
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Thu Sep 05 18:21:56 2024 +0200
@@ -557,15 +557,15 @@
                             int64_t since,
                             uint32_t limit) ORTHANC_OVERRIDE
     {
-      GetChanges2(target, done, since, -1, limit, ChangeType_INTERNAL_All);
+      GetChangesExtended(target, done, since, -1, limit, ChangeType_INTERNAL_All);
     }
 
-    virtual void GetChanges2(std::list<ServerIndexChange>& target /*out*/,
-                             bool& done /*out*/,
-                             int64_t since,
-                             int64_t to,
-                             uint32_t limit,
-                             ChangeType filterType) ORTHANC_OVERRIDE
+    virtual void GetChangesExtended(std::list<ServerIndexChange>& target /*out*/,
+                                    bool& done /*out*/,
+                                    int64_t since,
+                                    int64_t to,
+                                    uint32_t limit,
+                                    ChangeType filterType) ORTHANC_OVERRIDE
     {
       std::vector<std::string> filters;
       bool hasSince = false;
@@ -1410,7 +1410,7 @@
     // TODO: implement revisions in SQLite
     dbCapabilities_.SetFlushToDisk(true);
     dbCapabilities_.SetLabelsSupport(true);
-    dbCapabilities_.SetHasExtendedApiV1(true);
+    dbCapabilities_.SetHasExtendedChanges(true);
     db_.Open(path);
   }
 
@@ -1423,7 +1423,7 @@
     // TODO: implement revisions in SQLite
     dbCapabilities_.SetFlushToDisk(true);
     dbCapabilities_.SetLabelsSupport(true);
-    dbCapabilities_.SetHasExtendedApiV1(true);
+    dbCapabilities_.SetHasExtendedChanges(true);
     db_.OpenInMemory();
   }
 
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Thu Sep 05 18:21:56 2024 +0200
@@ -281,6 +281,10 @@
     }
     
     target["Last"] = static_cast<int>(last);
+    if (!log.empty())
+    {
+      target["First"] = static_cast<int>(log.front().GetSeq());
+    }
   }
 
 
@@ -1222,11 +1226,11 @@
   }
 
 
-  void StatelessDatabaseOperations::GetChanges2(Json::Value& target,
-                                                int64_t since,
-                                                int64_t to,                               
-                                                unsigned int maxResults,
-                                                ChangeType changeType)
+  void StatelessDatabaseOperations::GetChangesExtended(Json::Value& target,
+                                                       int64_t since,
+                                                       int64_t to,                               
+                                                       unsigned int maxResults,
+                                                       ChangeType changeType)
   {
     class Operations : public ReadOnlyOperationsT5<Json::Value&, int64_t, int64_t, unsigned int, unsigned int>
     {
@@ -1239,7 +1243,7 @@
         bool hasLast = false;
         int64_t last = 0;
 
-        transaction.GetChanges2(changes, done, tuple.get<1>(), tuple.get<2>(), tuple.get<3>(), static_cast<ChangeType>(tuple.get<4>()));
+        transaction.GetChangesExtended(changes, done, tuple.get<1>(), tuple.get<2>(), tuple.get<3>(), static_cast<ChangeType>(tuple.get<4>()));
         if (changes.empty())
         {
           last = transaction.GetLastChangeIndex();
@@ -3842,9 +3846,9 @@
     return db_.GetDatabaseCapabilities().HasLabelsSupport();
   }
 
-  bool StatelessDatabaseOperations::HasExtendedApiV1()
+  bool StatelessDatabaseOperations::HasExtendedChanges()
   {
     boost::shared_lock<boost::shared_mutex> lock(mutex_);
-    return db_.GetDatabaseCapabilities().HasExtendedApiV1();
+    return db_.GetDatabaseCapabilities().HasExtendedChanges();
   }
 }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Thu Sep 05 18:21:56 2024 +0200
@@ -247,14 +247,14 @@
         transaction_.GetChanges(target, done, since, limit);
       }
 
-      void GetChanges2(std::list<ServerIndexChange>& target /*out*/,
-                       bool& done /*out*/,
-                       int64_t since,
-                       int64_t to,
-                       uint32_t limit,
-                       ChangeType filterType)
+      void GetChangesExtended(std::list<ServerIndexChange>& target /*out*/,
+                              bool& done /*out*/,
+                              int64_t since,
+                              int64_t to,
+                              uint32_t limit,
+                              ChangeType filterType)
       {
-        transaction_.GetChanges2(target, done, since, to, limit, filterType);
+        transaction_.GetChangesExtended(target, done, since, to, limit, filterType);
       }
 
       void GetChildrenInternalId(std::list<int64_t>& target,
@@ -640,15 +640,15 @@
                     int64_t since,
                     uint32_t limit);
 
-    void GetChanges2(Json::Value& target,
-                     int64_t since,
-                     int64_t to,
-                     uint32_t limit,
-                     ChangeType filterType);
+    void GetChangesExtended(Json::Value& target,
+                            int64_t since,
+                            int64_t to,
+                            uint32_t limit,
+                            ChangeType filterType);
 
     void GetLastChange(Json::Value& target);
 
-    bool HasExtendedApiV1();
+    bool HasExtendedChanges();
 
     void GetExportedResources(Json::Value& target,
                               int64_t since,
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestChanges.cpp	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestChanges.cpp	Thu Sep 05 18:21:56 2024 +0200
@@ -29,6 +29,8 @@
 namespace Orthanc
 {
   // Changes API --------------------------------------------------------------
+  static const unsigned int DEFAULT_LIMIT = 100;
+  static const int64_t DEFAULT_TO = -1;
  
   static void GetSinceToAndLimit(int64_t& since,
                                  int64_t& to,
@@ -36,9 +38,6 @@
                                  bool& last,
                                  const RestApiGetCall& call)
   {
-    static const unsigned int DEFAULT_LIMIT = 100;
-    static const int64_t DEFAULT_TO = -1;
-    
     if (call.HasArgument("last"))
     {
       last = true;
@@ -70,57 +69,19 @@
         .SetTag("Tracking changes")
         .SetSummary("List changes")
         .SetDescription("Whenever Orthanc receives a new DICOM instance, this event is recorded in the so-called _Changes Log_. This enables remote scripts to react to the arrival of new DICOM resources. A typical application is auto-routing, where an external script waits for a new DICOM instance to arrive into Orthanc, then forward this instance to another modality.")
+        .SetHttpGetArgument("last", RestApiCallDocumentation::Type_Number, "Request only the last change id (this argument must be used alone)", false)
         .SetHttpGetArgument("limit", RestApiCallDocumentation::Type_Number, "Limit the number of results", false)
-        .SetHttpGetArgument("since", RestApiCallDocumentation::Type_Number, "Show only the resources since the provided index", false)
+        .SetHttpGetArgument("since", RestApiCallDocumentation::Type_Number, "Show only the resources since the provided index excluded", false)
+        .SetHttpGetArgument("to", RestApiCallDocumentation::Type_Number, "Show only the resources till the provided index included (only available if your DB backend supports ExtendedChanges)", false)
+        .SetHttpGetArgument("type", RestApiCallDocumentation::Type_String, "Show only the changes of the provided type (only available if your DB backend supports ExtendedChanges)", false)
         .AddAnswerType(MimeType_Json, "The list of changes")
         .SetAnswerField("Changes", RestApiCallDocumentation::Type_JsonListOfObjects, "The individual changes")
         .SetAnswerField("Done", RestApiCallDocumentation::Type_Boolean,
-                        "Whether the last reported change is the last of the full history")
+                        "Whether the last reported change is the last of the full history.")
         .SetAnswerField("Last", RestApiCallDocumentation::Type_Number,
                         "The index of the last reported change, can be used for the `since` argument in subsequent calls to this route")
-        .SetHttpGetSample("https://orthanc.uclouvain.be/demo/changes?since=0&limit=2", true);
-      return;
-    }
-    
-    ServerContext& context = OrthancRestApi::GetContext(call);
-
-    int64_t since;
-    int64_t toNotUsed;
-    unsigned int limit;
-    bool last;
-    GetSinceToAndLimit(since, toNotUsed, limit, last, call);
-
-    Json::Value result;
-    if (last)
-    {
-      context.GetIndex().GetLastChange(result);
-    }
-    else
-    {
-      context.GetIndex().GetChanges(result, since, limit);
-    }
-
-    call.GetOutput().AnswerJson(result);
-  }
-
-  static void GetChanges2(RestApiGetCall& call)
-  {
-    if (call.IsDocumentation())
-    {
-      call.GetDocumentation()
-        .SetTag("Tracking changes")
-        .SetSummary("List changes")
-        .SetDescription("Whenever Orthanc receives a new DICOM instance, this event is recorded in the so-called _Changes Log_. This enables remote scripts to react to the arrival of new DICOM resources. A typical application is auto-routing, where an external script waits for a new DICOM instance to arrive into Orthanc, then forward this instance to another modality.")
-        .SetHttpGetArgument("limit", RestApiCallDocumentation::Type_Number, "Limit the number of results", false)
-        .SetHttpGetArgument("since", RestApiCallDocumentation::Type_Number, "Show only the resources since the provided index", false)
-        .SetHttpGetArgument("to", RestApiCallDocumentation::Type_Number, "Show only the resources till the provided index", false)
-        .SetHttpGetArgument("type", RestApiCallDocumentation::Type_String, "Show only the changes of the provided type", false)
-        .AddAnswerType(MimeType_Json, "The list of changes")
-        .SetAnswerField("Changes", RestApiCallDocumentation::Type_JsonListOfObjects, "The individual changes")
-        .SetAnswerField("Done", RestApiCallDocumentation::Type_Boolean,
-                        "Whether the last reported change is the last of the full history")
-        .SetAnswerField("Last", RestApiCallDocumentation::Type_Number,
-                        "The index of the last reported change, can be used for the `since` argument in subsequent calls to this route")
+        .SetAnswerField("First", RestApiCallDocumentation::Type_Number,
+                        "The index of the first reported change, its value-1 can be used for the `to` argument in subsequent calls to this route when browsing the changes in reverse order")
         .SetHttpGetSample("https://orthanc.uclouvain.be/demo/changes?since=0&limit=2", true);
       return;
     }
@@ -145,14 +106,23 @@
     {
       context.GetIndex().GetLastChange(result);
     }
+    else if (context.GetIndex().HasExtendedChanges())
+    {
+      context.GetIndex().GetChangesExtended(result, since, to, limit, filterType);
+    }
     else
     {
-      if (filterType != ChangeType_INTERNAL_All && !context.GetIndex().HasExtendedApiV1())
+      if (filterType != ChangeType_INTERNAL_All)
       {
-        throw OrthancException(ErrorCode_ParameterOutOfRange, "Trying to filter changes while the Database backend does not support it (requires ExtendedApiV1)");
+        throw OrthancException(ErrorCode_ParameterOutOfRange, "CAPABILITIES: Trying to filter changes while the Database backend does not support it (requires a DB backend with support for ExtendedChanges)");
       }
 
-      context.GetIndex().GetChanges2(result, since, to, limit, filterType);
+      if (to != DEFAULT_TO)
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange, "CAPABILITIES: Trying to use the 'to' parameter in /changes while the Database backend does not support it (requires a DB backend with support for ExtendedChanges)");
+      }
+
+      context.GetIndex().GetChanges(result, since, limit);
     }
 
     call.GetOutput().AnswerJson(result);
@@ -239,9 +209,5 @@
     Register("/changes", DeleteChanges);
     Register("/exports", GetExports);
     Register("/exports", DeleteExports);
-    if (context_.GetIndex().HasExtendedApiV1())
-    {
-      Register("/extended-api-v1/changes", GetChanges2);
-    }
   }
 }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp	Thu Jul 04 07:40:58 2024 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp	Thu Sep 05 18:21:56 2024 +0200
@@ -92,7 +92,8 @@
     static const char* const MAXIMUM_STORAGE_MODE = "MaximumStorageMode";
     static const char* const USER_METADATA = "UserMetadata";
     static const char* const HAS_LABELS = "HasLabels";
-    static const char* const HAS_EXTENDED_API_V1 = "HasExtendedApiV1";
+    static const char* const CAPABILITIES = "Capabilities";
+    static const char* const HAS_EXTENDED_CHANGES = "HasExtendedChanges";
 
     if (call.IsDocumentation())
     {
@@ -139,8 +140,8 @@
                         "The configured UserMetadata (new in Orthanc 1.12.0)")
         .SetAnswerField(HAS_LABELS, RestApiCallDocumentation::Type_Boolean,
                         "Whether the database back-end supports labels (new in Orthanc 1.12.0)")
-        .SetAnswerField(HAS_EXTENDED_API_V1, RestApiCallDocumentation::Type_Boolean,
-                        "Whether the database back-end supports extended API v1 (new in Orthanc 1.13.0)")
+        .SetAnswerField(CAPABILITIES, RestApiCallDocumentation::Type_JsonObject,
+                        "Whether the back-end supports optional features like 'HasExtendedChanges' (new in Orthanc 1.12.5) ")
         .SetHttpGetSample("https://orthanc.uclouvain.be/demo/system", true);
       return;
     }
@@ -199,7 +200,8 @@
     GetUserMetadataConfiguration(result[USER_METADATA]);
 
     result[HAS_LABELS] = OrthancRestApi::GetIndex(call).HasLabelsSupport();
-    result[HAS_EXTENDED_API_V1] = OrthancRestApi::GetIndex(call).HasExtendedApiV1();
+    result[CAPABILITIES] = Json::objectValue;
+    result[CAPABILITIES][HAS_EXTENDED_CHANGES] = OrthancRestApi::GetIndex(call).HasExtendedChanges();
     
     call.GetOutput().AnswerJson(result);
   }