# HG changeset patch # User Sebastien Jodogne # Date 1713190404 -7200 # Node ID 12d8a1a266e968a038b2999eaf43e5c24c43ee54 # Parent dcbf0c7769454fa8854f45ddcb88a72d87fb591c introduction of FindRequest and FindResponse diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/CMakeLists.txt --- a/OrthancServer/CMakeLists.txt Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/CMakeLists.txt Mon Apr 15 16:13:24 2024 +0200 @@ -89,11 +89,15 @@ set(ORTHANC_SERVER_SOURCES ${CMAKE_SOURCE_DIR}/Sources/Database/BaseDatabaseWrapper.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/Compatibility/DatabaseLookup.cpp + ${CMAKE_SOURCE_DIR}/Sources/Database/Compatibility/GenericFind.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/Compatibility/ICreateInstance.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/Compatibility/IGetChildrenMetadata.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/Compatibility/ILookupResourceAndParent.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/Compatibility/ILookupResources.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/Compatibility/SetOfResources.cpp + ${CMAKE_SOURCE_DIR}/Sources/Database/FindRequest.cpp + ${CMAKE_SOURCE_DIR}/Sources/Database/FindResponse.cpp + ${CMAKE_SOURCE_DIR}/Sources/Database/OrthancIdentifiers.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/ResourcesContent.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/SQLiteDatabaseWrapper.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/StatelessDatabaseOperations.cpp diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -30,6 +30,7 @@ #include "../../../OrthancFramework/Sources/Logging.h" #include "../../../OrthancFramework/Sources/OrthancException.h" +#include "../../Sources/Database/Compatibility/GenericFind.h" #include "../../Sources/Database/Compatibility/ICreateInstance.h" #include "../../Sources/Database/Compatibility/IGetChildrenMetadata.h" #include "../../Sources/Database/Compatibility/ILookupResourceAndParent.h" @@ -1447,6 +1448,14 @@ { throw OrthancException(ErrorCode_InternalError); // Not supported } + + + virtual void ExecuteFind(FindResponse& response, + const FindRequest& request) ORTHANC_OVERRIDE + { + Compatibility::GenericFind find(*this); + find.Execute(response, request); + } }; diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -29,6 +29,7 @@ #include "../../../OrthancFramework/Sources/Logging.h" #include "../../../OrthancFramework/Sources/OrthancException.h" +#include "../../Sources/Database/Compatibility/GenericFind.h" #include "../../Sources/Database/ResourcesContent.h" #include "../../Sources/Database/VoidDatabaseListener.h" #include "PluginsEnumerations.h" @@ -1060,6 +1061,14 @@ { throw OrthancException(ErrorCode_InternalError); // Not supported } + + + virtual void ExecuteFind(FindResponse& response, + const FindRequest& request) ORTHANC_OVERRIDE + { + Compatibility::GenericFind find(*this); + find.Execute(response, request); + } }; diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -30,6 +30,7 @@ #include "../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" #include "../../../OrthancFramework/Sources/Logging.h" #include "../../../OrthancFramework/Sources/OrthancException.h" +#include "../../Sources/Database/Compatibility/GenericFind.h" #include "../../Sources/Database/ResourcesContent.h" #include "../../Sources/Database/VoidDatabaseListener.h" #include "../../Sources/ServerToolbox.h" @@ -1275,6 +1276,14 @@ { ListLabelsInternal(target, false, -1); } + + + virtual void ExecuteFind(FindResponse& response, + const FindRequest& request) ORTHANC_OVERRIDE + { + Compatibility::GenericFind find(*this); + find.Execute(response, request); + } }; diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/Compatibility/GenericFind.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -0,0 +1,106 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2024 Osimis S.A., Belgium + * Copyright (C) 2021-2024 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 . + **/ + + +#include "GenericFind.h" + +#include "../../../../OrthancFramework/Sources/OrthancException.h" + + +namespace Orthanc +{ + namespace Compatibility + { + void GenericFind::Execute(FindResponse& response, + const FindRequest& request) + { + if (request.GetResponseType() == FindRequest::ResponseType_OrthancIdentifiers && + !request.GetOrthancIdentifiers().HasPatientId() && + !request.GetOrthancIdentifiers().HasStudyId() && + !request.GetOrthancIdentifiers().HasSeriesId() && + !request.GetOrthancIdentifiers().HasInstanceId() && + request.GetTagConstraintsCount() == 0 && + request.GetMetadataMode() == FindRequest::MetadataMode_None && + !request.IsRetrieveTagsAtLevel(ResourceType_Patient) && + !request.IsRetrieveTagsAtLevel(ResourceType_Study) && + !request.IsRetrieveTagsAtLevel(ResourceType_Series) && + !request.IsRetrieveTagsAtLevel(ResourceType_Instance) && + request.GetTagOrdering().empty() && + request.GetLabels().empty() && + request.GetMetadataConstraints().empty()) + { + std::list ids; + + if (request.HasLimits()) + { + transaction_.GetAllPublicIds(ids, request.GetLevel(), request.GetLimitsSince(), request.GetLimitsCount()); + } + else + { + transaction_.GetAllPublicIds(ids, request.GetLevel()); + } + + for (std::list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + OrthancIdentifiers identifiers; + identifiers.SetLevel(request.GetLevel(), *it); + + response.Add(new FindResponse::Item(request.GetLevel(), identifiers)); + } + } + else + { + throw OrthancException(ErrorCode_NotImplemented); + } + + + /** + * Sanity checks + **/ + + for (size_t i = 0; i < response.GetSize(); i++) + { + const FindResponse::Item& item = response.GetItem(i); + + if (item.GetLevel() != request.GetLevel()) + { + throw OrthancException(ErrorCode_InternalError); + } + + switch (request.GetResponseType()) + { + case FindRequest::ResponseType_OrthancIdentifiers: + break; + + case FindRequest::ResponseType_DicomMap: + if (!item.HasDicomMap()) + { + throw OrthancException(ErrorCode_InternalError); + } + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + } + } +} diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/Compatibility/GenericFind.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/Compatibility/GenericFind.h Mon Apr 15 16:13:24 2024 +0200 @@ -0,0 +1,46 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2024 Osimis S.A., Belgium + * Copyright (C) 2021-2024 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 . + **/ + + +#pragma once + +#include "../IDatabaseWrapper.h" + +namespace Orthanc +{ + namespace Compatibility + { + class GenericFind : public boost::noncopyable + { + private: + IDatabaseWrapper::ITransaction& transaction_; + + public: + GenericFind(IDatabaseWrapper::ITransaction& transaction) : + transaction_(transaction) + { + } + + void Execute(FindResponse& response, + const FindRequest& request); + }; + } +} diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/FindRequest.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/FindRequest.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -0,0 +1,243 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2024 Osimis S.A., Belgium + * Copyright (C) 2021-2024 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 . + **/ + + +#include "FindRequest.h" + +#include "../../../OrthancFramework/Sources/OrthancException.h" + + +#include + +namespace Orthanc +{ + bool FindRequest::IsCompatibleLevel(ResourceType levelOfInterest) const + { + switch (level_) + { + case ResourceType_Patient: + return (levelOfInterest == ResourceType_Patient); + + case ResourceType_Study: + return (levelOfInterest == ResourceType_Patient || + levelOfInterest == ResourceType_Study); + + case ResourceType_Series: + return (levelOfInterest == ResourceType_Patient || + levelOfInterest == ResourceType_Study || + levelOfInterest == ResourceType_Series); + + case ResourceType_Instance: + return (levelOfInterest == ResourceType_Patient || + levelOfInterest == ResourceType_Study || + levelOfInterest == ResourceType_Series || + levelOfInterest == ResourceType_Instance); + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + FindRequest::FindRequest(ResourceType level) : + level_(level), + responseType_(ResponseType_OrthancIdentifiers), + hasLimits_(false), + limitsSince_(0), + limitsCount_(0), + metadataMode_(MetadataMode_None), + retrievePatientTags_(false), + retrieveStudyTags_(false), + retrieveSeriesTags_(false), + retrieveInstanceTags_(false) + { + } + + + FindRequest::~FindRequest() + { + for (std::deque::iterator it = tagConstraints_.begin(); it != tagConstraints_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + } + + + void FindRequest::AddTagConstraint(TagConstraint* constraint /* takes ownership */) + { + if (constraint == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + else + { + tagConstraints_.push_back(constraint); + } + } + + + const FindRequest::TagConstraint& FindRequest::GetTagConstraint(size_t index) const + { + if (index >= tagConstraints_.size()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + assert(tagConstraints_[index] != NULL); + return *tagConstraints_[index]; + } + } + + + void FindRequest::SetLimits(uint64_t since, + uint64_t count) + { + if (hasLimits_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + hasLimits_ = true; + limitsSince_ = since; + limitsCount_ = count; + } + } + + + uint64_t FindRequest::GetLimitsSince() const + { + if (hasLimits_) + { + return limitsSince_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + uint64_t FindRequest::GetLimitsCount() const + { + if (hasLimits_) + { + return limitsCount_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + void FindRequest::SetRetrieveTagsAtLevel(ResourceType levelOfInterest, + bool retrieve) + { + if (!IsCompatibleLevel(levelOfInterest)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + switch (levelOfInterest) + { + case ResourceType_Patient: + retrievePatientTags_ = true; + break; + + case ResourceType_Study: + retrieveStudyTags_ = true; + break; + + case ResourceType_Series: + retrieveSeriesTags_ = true; + break; + + case ResourceType_Instance: + retrieveInstanceTags_ = true; + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + bool FindRequest::IsRetrieveTagsAtLevel(ResourceType levelOfInterest) const + { + switch (levelOfInterest) + { + case ResourceType_Patient: + return retrievePatientTags_; + + case ResourceType_Study: + return retrieveStudyTags_; + + case ResourceType_Series: + return retrieveSeriesTags_; + + case ResourceType_Instance: + return retrieveInstanceTags_; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + void FindRequest::SetTagOrdering(DicomTag tag, + Ordering ordering) + { + switch (ordering) + { + case Ordering_None: + tagOrdering_.erase(tag); + break; + + case Ordering_Ascending: + tagOrdering_[tag] = Ordering_Ascending; + break; + + case Ordering_Descending: + tagOrdering_[tag] = Ordering_Descending; + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + void FindRequest::AddMetadataConstraint(MetadataType metadata, + const std::string& value) + { + if (metadataConstraints_.find(metadata) == metadataConstraints_.end()) + { + metadataConstraints_[metadata] = value; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } +} diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/FindRequest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/FindRequest.h Mon Apr 15 16:13:24 2024 +0200 @@ -0,0 +1,361 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2024 Osimis S.A., Belgium + * Copyright (C) 2021-2024 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 . + **/ + + +#pragma once + +#include "../../../OrthancFramework/Sources/DicomFormat/DicomTag.h" +#include "../ServerEnumerations.h" +#include "OrthancIdentifiers.h" + +#include +#include +#include + + +namespace Orthanc +{ + class FindRequest : public boost::noncopyable + { + public: + enum ResponseType + { + ResponseType_OrthancIdentifiers, + ResponseType_DicomMap + }; + + enum ConstraintType + { + ConstraintType_Mandatory, + ConstraintType_Equality, + ConstraintType_Range, + ConstraintType_Wildcard, + ConstraintType_List + }; + + enum MetadataMode + { + MetadataMode_None, + MetadataMode_List, + MetadataMode_Retrieve + }; + + enum Ordering + { + Ordering_Ascending, + Ordering_Descending, + Ordering_None + }; + + + class TagConstraint : public boost::noncopyable + { + private: + DicomTag tag_; + + public: + TagConstraint(DicomTag tag) : + tag_(tag) + { + } + + virtual ~TagConstraint() + { + } + + virtual DicomTag GetTag() const + { + return tag_; + } + + virtual ConstraintType GetType() const = 0; + + virtual bool IsCaseSensitive() const = 0; // Needed for PN VR + }; + + + class MandatoryConstraint : public TagConstraint + { + public: + virtual ConstraintType GetType() const ORTHANC_OVERRIDE + { + return ConstraintType_Mandatory; + } + }; + + + class StringConstraint : public TagConstraint + { + private: + bool caseSensitive_; + + public: + StringConstraint(DicomTag tag, + bool caseSensitive) : + TagConstraint(tag), + caseSensitive_(caseSensitive) + { + } + + bool IsCaseSensitive() const + { + return caseSensitive_; + } + }; + + + class EqualityConstraint : public StringConstraint + { + private: + std::string value_; + + public: + explicit EqualityConstraint(DicomTag tag, + bool caseSensitive, + const std::string& value) : + StringConstraint(tag, caseSensitive), + value_(value) + { + } + + virtual ConstraintType GetType() const ORTHANC_OVERRIDE + { + return ConstraintType_Equality; + } + + const std::string& GetValue() const + { + return value_; + } + }; + + + class RangeConstraint : public StringConstraint + { + private: + std::string start_; + std::string end_; // Inclusive + + public: + RangeConstraint(DicomTag tag, + bool caseSensitive, + const std::string& start, + const std::string& end) : + StringConstraint(tag, caseSensitive), + start_(start), + end_(end) + { + } + + virtual ConstraintType GetType() const ORTHANC_OVERRIDE + { + return ConstraintType_Range; + } + + const std::string& GetStart() const + { + return start_; + } + + const std::string& GetEnd() const + { + return end_; + } + }; + + + class WildcardConstraint : public StringConstraint + { + private: + std::string value_; + + public: + explicit WildcardConstraint(DicomTag& tag, + bool caseSensitive, + const std::string& value) : + StringConstraint(tag, caseSensitive), + value_(value) + { + } + + virtual ConstraintType GetType() const ORTHANC_OVERRIDE + { + return ConstraintType_Wildcard; + } + + const std::string& GetValue() const + { + return value_; + } + }; + + + class ListConstraint : public StringConstraint + { + private: + std::set values_; + + public: + ListConstraint(DicomTag tag, + bool caseSensitive) : + StringConstraint(tag, caseSensitive) + { + } + + virtual ConstraintType GetType() const ORTHANC_OVERRIDE + { + return ConstraintType_List; + } + + const std::set& GetValues() const + { + return values_; + } + }; + + + private: + ResourceType level_; + ResponseType responseType_; + OrthancIdentifiers orthancIdentifiers_; + std::deque tagConstraints_; + bool hasLimits_; + uint64_t limitsSince_; + uint64_t limitsCount_; + MetadataMode metadataMode_; + bool retrievePatientTags_; + bool retrieveStudyTags_; + bool retrieveSeriesTags_; + bool retrieveInstanceTags_; + std::map tagOrdering_; + std::set labels_; + std::map metadataConstraints_; + + bool IsCompatibleLevel(ResourceType levelOfInterest) const; + + public: + FindRequest(ResourceType level); + + ~FindRequest(); + + ResourceType GetLevel() const + { + return level_; + } + + void SetResponseType(ResponseType type) + { + responseType_ = type; + } + + ResponseType GetResponseType() const + { + return responseType_; + } + + void SetOrthancPatientId(const std::string& id) + { + orthancIdentifiers_.SetPatientId(id); + } + + void SetOrthancStudyId(const std::string& id) + { + orthancIdentifiers_.SetStudyId(id); + } + + void SetOrthancSeriesId(const std::string& id) + { + orthancIdentifiers_.SetSeriesId(id); + } + + void SetOrthancInstanceId(const std::string& id) + { + orthancIdentifiers_.SetInstanceId(id); + } + + const OrthancIdentifiers& GetOrthancIdentifiers() const + { + return orthancIdentifiers_; + } + + void AddTagConstraint(TagConstraint* constraint /* takes ownership */); + + size_t GetTagConstraintsCount() const + { + return tagConstraints_.size(); + } + + const TagConstraint& GetTagConstraint(size_t index) const; + + void SetLimits(uint64_t since, + uint64_t count); + + bool HasLimits() const + { + return hasLimits_; + } + + uint64_t GetLimitsSince() const; + + uint64_t GetLimitsCount() const; + + void SetMetadataMode(MetadataMode mode) + { + metadataMode_ = mode; + } + + MetadataMode GetMetadataMode() const + { + return metadataMode_; + } + + void SetRetrieveTagsAtLevel(ResourceType levelOfInterest, + bool retrieve); + + bool IsRetrieveTagsAtLevel(ResourceType levelOfInterest) const; + + void SetTagOrdering(DicomTag tag, + Ordering ordering); + + const std::map& GetTagOrdering() const + { + return tagOrdering_; + } + + void AddLabel(const std::string& label) + { + labels_.insert(label); + } + + const std::set& GetLabels() const + { + return labels_; + } + + void AddMetadataConstraint(MetadataType metadata, + const std::string& value); + + const std::map& GetMetadataConstraints() const + { + return metadataConstraints_; + } + }; +} diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/FindResponse.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/FindResponse.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -0,0 +1,223 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2024 Osimis S.A., Belgium + * Copyright (C) 2021-2024 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 . + **/ + + +#include "FindResponse.h" + +#include "../../../OrthancFramework/Sources/DicomFormat/DicomInstanceHasher.h" +#include "../../../OrthancFramework/Sources/OrthancException.h" + +#include + + +namespace Orthanc +{ + static void ExtractOrthancIdentifiers(OrthancIdentifiers& identifiers, + ResourceType level, + const DicomMap& dicom) + { + switch (level) + { + case ResourceType_Patient: + { + std::string patientId; + if (!dicom.LookupStringValue(patientId, Orthanc::DICOM_TAG_PATIENT_ID, false)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + DicomInstanceHasher hasher(patientId, "", "", ""); + identifiers.SetPatientId(hasher.HashPatient()); + } + break; + } + + case ResourceType_Study: + { + std::string patientId, studyInstanceUid; + if (!dicom.LookupStringValue(patientId, Orthanc::DICOM_TAG_PATIENT_ID, false) || + !dicom.LookupStringValue(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + DicomInstanceHasher hasher(patientId, studyInstanceUid, "", ""); + identifiers.SetPatientId(hasher.HashPatient()); + identifiers.SetStudyId(hasher.HashStudy()); + } + break; + } + + case ResourceType_Series: + { + std::string patientId, studyInstanceUid, seriesInstanceUid; + if (!dicom.LookupStringValue(patientId, Orthanc::DICOM_TAG_PATIENT_ID, false) || + !dicom.LookupStringValue(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) || + !dicom.LookupStringValue(seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + DicomInstanceHasher hasher(patientId, studyInstanceUid, seriesInstanceUid, ""); + identifiers.SetPatientId(hasher.HashPatient()); + identifiers.SetStudyId(hasher.HashStudy()); + identifiers.SetSeriesId(hasher.HashSeries()); + } + break; + } + + case ResourceType_Instance: + { + std::string patientId, studyInstanceUid, seriesInstanceUid, sopInstanceUid; + if (!dicom.LookupStringValue(patientId, Orthanc::DICOM_TAG_PATIENT_ID, false) || + !dicom.LookupStringValue(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) || + !dicom.LookupStringValue(seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) || + !dicom.LookupStringValue(sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + DicomInstanceHasher hasher(patientId, studyInstanceUid, seriesInstanceUid, sopInstanceUid); + identifiers.SetPatientId(hasher.HashPatient()); + identifiers.SetStudyId(hasher.HashStudy()); + identifiers.SetSeriesId(hasher.HashSeries()); + identifiers.SetInstanceId(hasher.HashInstance()); + } + break; + } + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + FindResponse::Item::Item(ResourceType level, + DicomMap* dicomMap /* takes ownership */) : + level_(level), + dicomMap_(dicomMap) + { + if (dicomMap == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + else + { + ExtractOrthancIdentifiers(identifiers_, level, *dicomMap); + } + } + + + void FindResponse::Item::AddMetadata(MetadataType metadata, + const std::string& value) + { + if (metadata_.find(metadata) != metadata_.end()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); // Metadata already present + } + else + { + metadata_[metadata] = value; + } + } + + + bool FindResponse::Item::LookupMetadata(std::string& value, + MetadataType metadata) const + { + std::map::const_iterator found = metadata_.find(metadata); + + if (found == metadata_.end()) + { + return false; + } + else + { + value = found->second; + return true; + } + } + + + void FindResponse::Item::ListMetadata(std::set target) const + { + target.clear(); + + for (std::map::const_iterator it = metadata_.begin(); it != metadata_.end(); ++it) + { + target.insert(it->first); + } + } + + + const DicomMap& FindResponse::Item::GetDicomMap() const + { + if (dicomMap_.get() == NULL) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + return *dicomMap_; + } + } + + + FindResponse::~FindResponse() + { + for (size_t i = 0; i < items_.size(); i++) + { + assert(items_[i] != NULL); + delete items_[i]; + } + } + + + void FindResponse::Add(Item* item /* takes ownership */) + { + if (item == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + else + { + items_.push_back(item); + } + } + + + const FindResponse::Item& FindResponse::GetItem(size_t index) const + { + if (index >= items_.size()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + assert(items_[index] != NULL); + return *items_[index]; + } + } +} diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/FindResponse.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/FindResponse.h Mon Apr 15 16:13:24 2024 +0200 @@ -0,0 +1,105 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2024 Osimis S.A., Belgium + * Copyright (C) 2021-2024 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 . + **/ + + +#pragma once + +#include "../../../OrthancFramework/Sources/DicomFormat/DicomMap.h" +#include "../ServerEnumerations.h" +#include "OrthancIdentifiers.h" + +#include +#include +#include +#include + + +namespace Orthanc +{ + class FindResponse : public boost::noncopyable + { + public: + class Item : public boost::noncopyable + { + private: + ResourceType level_; + OrthancIdentifiers identifiers_; + std::map metadata_; + std::unique_ptr dicomMap_; + + public: + Item(ResourceType level, + const OrthancIdentifiers& identifiers) : + level_(level), + identifiers_(identifiers) + { + } + + Item(ResourceType level, + DicomMap* dicomMap /* takes ownership */); + + ResourceType GetLevel() const + { + return level_; + } + + const OrthancIdentifiers& GetIdentifiers() const + { + return identifiers_; + } + + void AddMetadata(MetadataType metadata, + const std::string& value); + + bool HasMetadata(MetadataType metadata) const + { + return metadata_.find(metadata) != metadata_.end(); + } + + bool LookupMetadata(std::string& value, + MetadataType metadata) const; + + void ListMetadata(std::set metadata) const; + + bool HasDicomMap() const + { + return dicomMap_.get() != NULL; + } + + const DicomMap& GetDicomMap() const; + }; + + private: + std::deque items_; + + public: + ~FindResponse(); + + void Add(Item* item /* takes ownership */); + + size_t GetSize() const + { + return items_.size(); + } + + const Item& GetItem(size_t index) const; + }; +} diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/IDatabaseWrapper.h --- a/OrthancServer/Sources/Database/IDatabaseWrapper.h Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h Mon Apr 15 16:13:24 2024 +0200 @@ -28,6 +28,8 @@ #include "../ExportedResource.h" #include "../Search/ISqlLookupFormatter.h" #include "../ServerIndexChange.h" +#include "FindRequest.h" +#include "FindResponse.h" #include "IDatabaseListener.h" #include @@ -350,6 +352,13 @@ int64_t& instancesCount, int64_t& compressedSize, int64_t& uncompressedSize) = 0; + + /** + * Primitives introduced in Orthanc 1.12.4 + **/ + + virtual void ExecuteFind(FindResponse& response, + const FindRequest& request) = 0; }; diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/OrthancIdentifiers.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/OrthancIdentifiers.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -0,0 +1,242 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2024 Osimis S.A., Belgium + * Copyright (C) 2021-2024 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 . + **/ + + +#include "FindRequest.h" + +#include "../../../OrthancFramework/Sources/OrthancException.h" + + +namespace Orthanc +{ + OrthancIdentifiers::OrthancIdentifiers(const OrthancIdentifiers& other) + { + if (other.HasPatientId()) + { + SetPatientId(other.GetPatientId()); + } + + if (other.HasStudyId()) + { + SetStudyId(other.GetStudyId()); + } + + if (other.HasSeriesId()) + { + SetSeriesId(other.GetSeriesId()); + } + + if (other.HasInstanceId()) + { + SetInstanceId(other.GetInstanceId()); + } + } + + + void OrthancIdentifiers::SetPatientId(const std::string& id) + { + if (HasPatientId()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + patientId_.reset(new std::string(id)); + } + } + + + const std::string& OrthancIdentifiers::GetPatientId() const + { + if (HasPatientId()) + { + return *patientId_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + void OrthancIdentifiers::SetStudyId(const std::string& id) + { + if (HasStudyId()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + studyId_.reset(new std::string(id)); + } + } + + + const std::string& OrthancIdentifiers::GetStudyId() const + { + if (HasStudyId()) + { + return *studyId_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + void OrthancIdentifiers::SetSeriesId(const std::string& id) + { + if (HasSeriesId()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + seriesId_.reset(new std::string(id)); + } + } + + + const std::string& OrthancIdentifiers::GetSeriesId() const + { + if (HasSeriesId()) + { + return *seriesId_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + void OrthancIdentifiers::SetInstanceId(const std::string& id) + { + if (HasInstanceId()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + instanceId_.reset(new std::string(id)); + } + } + + + const std::string& OrthancIdentifiers::GetInstanceId() const + { + if (HasInstanceId()) + { + return *instanceId_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + ResourceType OrthancIdentifiers::DetectLevel() const + { + if (HasPatientId() && + !HasStudyId() && + !HasSeriesId() && + !HasInstanceId()) + { + return ResourceType_Patient; + } + else if (HasPatientId() && + HasStudyId() && + !HasSeriesId() && + !HasInstanceId()) + { + return ResourceType_Study; + } + else if (HasPatientId() && + HasStudyId() && + HasSeriesId() && + !HasInstanceId()) + { + return ResourceType_Series; + } + else if (HasPatientId() && + HasStudyId() && + HasSeriesId() && + HasInstanceId()) + { + return ResourceType_Instance; + } + else + { + throw OrthancException(ErrorCode_InexistentItem); + } + } + + + void OrthancIdentifiers::SetLevel(ResourceType level, + const std::string id) + { + switch (level) + { + case ResourceType_Patient: + SetPatientId(id); + break; + + case ResourceType_Study: + SetStudyId(id); + break; + + case ResourceType_Series: + SetSeriesId(id); + break; + + case ResourceType_Instance: + SetInstanceId(id); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + std::string OrthancIdentifiers::GetLevel(ResourceType level) const + { + switch (level) + { + case ResourceType_Patient: + return GetPatientId(); + + case ResourceType_Study: + return GetStudyId(); + + case ResourceType_Series: + return GetSeriesId(); + + case ResourceType_Instance: + return GetInstanceId(); + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } +} diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/OrthancIdentifiers.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/OrthancIdentifiers.h Mon Apr 15 16:13:24 2024 +0200 @@ -0,0 +1,92 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2024 Osimis S.A., Belgium + * Copyright (C) 2021-2024 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 . + **/ + + +#pragma once + +#include "../../../OrthancFramework/Sources/Compatibility.h" +#include "../../../OrthancFramework/Sources/Enumerations.h" + +#include +#include + + +namespace Orthanc +{ + class OrthancIdentifiers : public boost::noncopyable + { + private: + std::unique_ptr patientId_; + std::unique_ptr studyId_; + std::unique_ptr seriesId_; + std::unique_ptr instanceId_; + + public: + OrthancIdentifiers() + { + } + + OrthancIdentifiers(const OrthancIdentifiers& other); + + void SetPatientId(const std::string& id); + + bool HasPatientId() const + { + return patientId_.get() != NULL; + } + + const std::string& GetPatientId() const; + + void SetStudyId(const std::string& id); + + bool HasStudyId() const + { + return studyId_.get() != NULL; + } + + const std::string& GetStudyId() const; + + void SetSeriesId(const std::string& id); + + bool HasSeriesId() const + { + return seriesId_.get() != NULL; + } + + const std::string& GetSeriesId() const; + + void SetInstanceId(const std::string& id); + + bool HasInstanceId() const + { + return instanceId_.get() != NULL; + } + + const std::string& GetInstanceId() const; + + ResourceType DetectLevel() const; + + void SetLevel(ResourceType level, + const std::string id); + + std::string GetLevel(ResourceType level) const; + }; +} diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp --- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -28,6 +28,7 @@ #include "../../../OrthancFramework/Sources/SQLite/Transaction.h" #include "../Search/ISqlLookupFormatter.h" #include "../ServerToolbox.h" +#include "Compatibility/GenericFind.h" #include "Compatibility/ICreateInstance.h" #include "Compatibility/IGetChildrenMetadata.h" #include "Compatibility/ILookupResourceAndParent.h" @@ -1137,6 +1138,14 @@ target.insert(s.ColumnString(0)); } } + + + virtual void ExecuteFind(FindResponse& response, + const FindRequest& request) ORTHANC_OVERRIDE + { + Compatibility::GenericFind find(*this); + find.Execute(response, request); + } }; diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -3775,4 +3775,22 @@ boost::shared_lock lock(mutex_); return db_.GetDatabaseCapabilities().HasLabelsSupport(); } + + + void StatelessDatabaseOperations::ExecuteFind(FindResponse& response, + const FindRequest& request) + { + class Operations : public ReadOnlyOperationsT2 + { + public: + virtual void ApplyTuple(ReadOnlyTransaction& transaction, + const Tuple& tuple) ORTHANC_OVERRIDE + { + transaction.ExecuteFind(tuple.get<0>(), tuple.get<1>()); + } + }; + + Operations operations; + operations.Apply(*this, response, request); + } } diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Mon Apr 15 16:13:24 2024 +0200 @@ -376,6 +376,12 @@ { transaction_.ListAllLabels(target); } + + void ExecuteFind(FindResponse& response, + const FindRequest& request) + { + transaction_.ExecuteFind(response, request); + } }; @@ -798,5 +804,8 @@ const std::set& labels); bool HasLabelsSupport(); + + void ExecuteFind(FindResponse& response, + const FindRequest& request); }; } diff -r dcbf0c776945 -r 12d8a1a266e9 OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Mar 29 23:23:01 2024 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Mon Apr 15 16:13:24 2024 +0200 @@ -126,7 +126,7 @@ // List all the patients, studies, series or instances ---------------------- - static void AnswerListOfResources(RestApiOutput& output, + static void AnswerListOfResources1(RestApiOutput& output, ServerContext& context, const std::list& resources, const std::map& instancesIds, // optional: the id of an instance for each found resource. @@ -180,7 +180,7 @@ } - static void AnswerListOfResources(RestApiOutput& output, + static void AnswerListOfResources2(RestApiOutput& output, ServerContext& context, const std::list& resources, ResourceType level, @@ -193,7 +193,7 @@ std::map > unusedResourcesMainDicomTags; std::map > unusedResourcesDicomAsJson; - AnswerListOfResources(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess); + AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess); } @@ -223,41 +223,141 @@ ServerIndex& index = OrthancRestApi::GetIndex(call); ServerContext& context = OrthancRestApi::GetContext(call); - std::list result; - - std::set requestedTags; - OrthancRestApi::GetRequestedTags(requestedTags, call); - - if (call.HasArgument("limit") || - call.HasArgument("since")) + if (true) { - if (!call.HasArgument("limit")) + /** + * EXPERIMENTAL VERSION + **/ + + const bool expand = (call.HasArgument("expand") && + call.GetBooleanArgument("expand", true)); + + FindRequest request(resourceType); + +#if 0 + // TODO - This version should be executed if no disk access is needed + if (expand) + { + request.SetResponseType(FindRequest::ResponseType_DicomMap); + request.SetMetadataMode(FindRequest::MetadataMode_Retrieve); + request.SetRetrieveTagsAtLevel(resourceType, true); + + if (resourceType == ResourceType_Study) + { + request.SetRetrieveTagsAtLevel(ResourceType_Patient, true); + } + } + else + { + request.SetResponseType(FindRequest::ResponseType_OrthancIdentifiers); + request.SetMetadataMode(FindRequest::MetadataMode_None); + } +#else + request.SetResponseType(FindRequest::ResponseType_OrthancIdentifiers); + request.SetMetadataMode(FindRequest::MetadataMode_None); +#endif + + if (call.HasArgument("limit") || + call.HasArgument("since")) { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"limit\" argument for GET request against: " + - call.FlattenUri()); + if (!call.HasArgument("limit")) + { + throw OrthancException(ErrorCode_BadRequest, + "Missing \"limit\" argument for GET request against: " + + call.FlattenUri()); + } + + if (!call.HasArgument("since")) + { + throw OrthancException(ErrorCode_BadRequest, + "Missing \"since\" argument for GET request against: " + + call.FlattenUri()); + } + + uint64_t since = boost::lexical_cast(call.GetArgument("since", "")); + uint64_t limit = boost::lexical_cast(call.GetArgument("limit", "")); + request.SetLimits(since, limit); } - if (!call.HasArgument("since")) + FindResponse response; + index.ExecuteFind(response, request); + + std::set requestedTags; + OrthancRestApi::GetRequestedTags(requestedTags, call); + + const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); + + Json::Value answer = Json::arrayValue; + + for (size_t i = 0; i < response.GetSize(); i++) { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"since\" argument for GET request against: " + - call.FlattenUri()); + std::string resourceId = response.GetItem(i).GetIdentifiers().GetLevel(resourceType); + + if (expand) + { + Json::Value expanded; + + context.ExpandResource(expanded, resourceId, resourceType, format, requestedTags, true /* allowStorageAccess */); + + if (expanded.type() == Json::objectValue) + { + answer.append(expanded); + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + else + { + answer.append(resourceId); + } } - size_t since = boost::lexical_cast(call.GetArgument("since", "")); - size_t limit = boost::lexical_cast(call.GetArgument("limit", "")); - index.GetAllUuids(result, resourceType, since, limit); + call.GetOutput().AnswerJson(answer); } else { - index.GetAllUuids(result, resourceType); + /** + * VERSION IN ORTHANC <= 1.12.3 + **/ + + std::list result; + + std::set requestedTags; + OrthancRestApi::GetRequestedTags(requestedTags, call); + + if (call.HasArgument("limit") || + call.HasArgument("since")) + { + if (!call.HasArgument("limit")) + { + throw OrthancException(ErrorCode_BadRequest, + "Missing \"limit\" argument for GET request against: " + + call.FlattenUri()); + } + + if (!call.HasArgument("since")) + { + throw OrthancException(ErrorCode_BadRequest, + "Missing \"since\" argument for GET request against: " + + call.FlattenUri()); + } + + size_t since = boost::lexical_cast(call.GetArgument("since", "")); + size_t limit = boost::lexical_cast(call.GetArgument("limit", "")); + index.GetAllUuids(result, resourceType, since, limit); + } + else + { + index.GetAllUuids(result, resourceType); + } + + AnswerListOfResources2(call.GetOutput(), context, result, resourceType, call.HasArgument("expand") && call.GetBooleanArgument("expand", true), + OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), + requestedTags, + true /* allowStorageAccess */); } - - AnswerListOfResources(call.GetOutput(), context, result, resourceType, call.HasArgument("expand") && call.GetBooleanArgument("expand", true), - OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), - requestedTags, - true /* allowStorageAccess */); } @@ -3106,7 +3206,7 @@ bool expand, const std::set& requestedTags) const { - AnswerListOfResources(output, context, resources_, instancesIds_, resourcesMainDicomTags_, resourcesDicomAsJson_, level, expand, format_, requestedTags, IsStorageAccessAllowedForAnswers(findStorageAccessMode_)); + AnswerListOfResources1(output, context, resources_, instancesIds_, resourcesMainDicomTags_, resourcesDicomAsJson_, level, expand, format_, requestedTags, IsStorageAccessAllowedForAnswers(findStorageAccessMode_)); } }; } @@ -3386,7 +3486,7 @@ a.splice(a.begin(), b); } - AnswerListOfResources(call.GetOutput(), context, a, type, !call.HasArgument("expand") || call.GetBooleanArgument("expand", false), // this "expand" is the only one to have a false default value to keep backward compatibility + AnswerListOfResources2(call.GetOutput(), context, a, type, !call.HasArgument("expand") || call.GetBooleanArgument("expand", false), // this "expand" is the only one to have a false default value to keep backward compatibility OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), requestedTags, true /* allowStorageAccess */);