# HG changeset patch # User Alain Mazy # Date 1725553316 -7200 # Node ID 5463c3ae323536669e0bc0ee0b19df2230eef7e9 # Parent 3765085693e5bb02c4973154702933bc061cff82 refactored extended /changes diff -r 3765085693e5 -r 5463c3ae3235 NEWS --- 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 diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp --- 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& target /*out*/, - bool& done /*out*/, - int64_t since, - int64_t to, - uint32_t limit, - ChangeType changeType) ORTHANC_OVERRIDE + virtual void GetChangesExtended(std::list& 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; diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto --- 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 { diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Sources/Database/BaseDatabaseWrapper.cpp --- 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& target /*out*/, - bool& done /*out*/, - int64_t since, - int64_t to, - uint32_t limit, - ChangeType filterType) + void BaseDatabaseWrapper::BaseTransaction::GetChangesExtended(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + int64_t to, + uint32_t limit, + ChangeType filterType) { throw OrthancException(ErrorCode_NotImplemented); // Not supported } diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Sources/Database/BaseDatabaseWrapper.h --- 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& target /*out*/, - bool& done /*out*/, - int64_t since, - int64_t to, - uint32_t limit, - ChangeType filterType) ORTHANC_OVERRIDE; + virtual void GetChangesExtended(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + int64_t to, + uint32_t limit, + ChangeType filterType) ORTHANC_OVERRIDE; }; diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Sources/Database/IDatabaseWrapper.h --- 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& target /*out*/, - bool& done /*out*/, - int64_t since, - int64_t to, - uint32_t limit, - ChangeType filterType) = 0; + virtual void GetChangesExtended(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + int64_t to, + uint32_t limit, + ChangeType filterType) = 0; }; diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp --- 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& target /*out*/, - bool& done /*out*/, - int64_t since, - int64_t to, - uint32_t limit, - ChangeType filterType) ORTHANC_OVERRIDE + virtual void GetChangesExtended(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + int64_t to, + uint32_t limit, + ChangeType filterType) ORTHANC_OVERRIDE { std::vector 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(); } diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- 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(last); + if (!log.empty()) + { + target["First"] = static_cast(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 { @@ -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(tuple.get<4>())); + transaction.GetChangesExtended(changes, done, tuple.get<1>(), tuple.get<2>(), tuple.get<3>(), static_cast(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 lock(mutex_); - return db_.GetDatabaseCapabilities().HasExtendedApiV1(); + return db_.GetDatabaseCapabilities().HasExtendedChanges(); } } diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- 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& target /*out*/, - bool& done /*out*/, - int64_t since, - int64_t to, - uint32_t limit, - ChangeType filterType) + void GetChangesExtended(std::list& 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& 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, diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Sources/OrthancRestApi/OrthancRestChanges.cpp --- 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); - } } } diff -r 3765085693e5 -r 5463c3ae3235 OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp --- 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); }