From c33e8f4ce2ee5d0a4396dd1e5040eb27170fab8b Mon Sep 17 00:00:00 2001 From: Patryk Kisielewski Date: Mon, 25 May 2026 14:03:19 +0200 Subject: [PATCH] feat(kvdb): add findEntry returning optional instead of throwing on missing key Adds KvdbApi::findEntry backed by a new kvdbEntryFind server call that returns std::optional when the key is absent, instead of throwing KvdbEntryDoesNotExistException. Updates PrivmxFS::access and getFileId in the search module to use findEntry, removing the try/catch exception-based control flow used previously to handle legitimately missing entries. --- .../privmx/endpoint/kvdb/KvdbApiImpl.hpp | 1 + .../privmx/endpoint/kvdb/ServerApi.hpp | 1 + .../privmx/endpoint/kvdb/ServerTypes.hpp | 3 +++ .../kvdb/varinterface/KvdbApiVarInterface.hpp | 2 ++ .../privmx/endpoint/kvdb/KvdbApi.hpp | 9 ++++++++ endpoint/kvdb/src/KvdbApi.cpp | 11 +++++++++ endpoint/kvdb/src/KvdbApiImpl.cpp | 12 ++++++++++ endpoint/kvdb/src/ServerApi.cpp | 4 ++++ .../src/varinterface/KvdbApiVarInterface.cpp | 11 ++++++++- endpoint/search/src/PrivmxFS.cpp | 23 ++++++++----------- 10 files changed, 63 insertions(+), 14 deletions(-) diff --git a/endpoint/kvdb/include/privmx/endpoint/kvdb/KvdbApiImpl.hpp b/endpoint/kvdb/include/privmx/endpoint/kvdb/KvdbApiImpl.hpp index d8640c5f..52d13d78 100644 --- a/endpoint/kvdb/include/privmx/endpoint/kvdb/KvdbApiImpl.hpp +++ b/endpoint/kvdb/include/privmx/endpoint/kvdb/KvdbApiImpl.hpp @@ -79,6 +79,7 @@ class KvdbApiImpl : public privmx::utils::ManualManagedClass, prote ); KvdbEntry getEntry(const std::string& kvdbId, const std::string& key); + std::optional findEntry(const std::string& kvdbId, const std::string& key); bool hasEntry(const std::string& kvdbId, const std::string& key); core::PagingList listEntriesKeys(const std::string& kvdbId, const core::PagingQuery& pagingQuery); core::PagingList listEntries(const std::string& kvdbId, const core::PagingQuery& pagingQuery); diff --git a/endpoint/kvdb/include/privmx/endpoint/kvdb/ServerApi.hpp b/endpoint/kvdb/include/privmx/endpoint/kvdb/ServerApi.hpp index 1746a3df..d0298ea0 100644 --- a/endpoint/kvdb/include/privmx/endpoint/kvdb/ServerApi.hpp +++ b/endpoint/kvdb/include/privmx/endpoint/kvdb/ServerApi.hpp @@ -35,6 +35,7 @@ class ServerApi { server::KvdbGetResult kvdbGet(server::KvdbGetModel model); server::KvdbListResult kvdbList(server::KvdbListModel model); server::KvdbEntryGetResult kvdbEntryGet(server::KvdbEntryGetModel model); + server::KvdbEntryFindResult kvdbEntryFind(server::KvdbEntryGetModel model); void kvdbEntrySet(server::KvdbEntrySetModel model); void kvdbEntryDelete(server::KvdbEntryDeleteModel model); server::KvdbListKeysResult kvdbListKeys(server::KvdbListKeysModel model); diff --git a/endpoint/kvdb/include/privmx/endpoint/kvdb/ServerTypes.hpp b/endpoint/kvdb/include/privmx/endpoint/kvdb/ServerTypes.hpp index ff705943..cc29b240 100644 --- a/endpoint/kvdb/include/privmx/endpoint/kvdb/ServerTypes.hpp +++ b/endpoint/kvdb/include/privmx/endpoint/kvdb/ServerTypes.hpp @@ -143,6 +143,9 @@ JSON_STRUCT(KvdbEntryGetModel, KVDB_ENTRY_GET_MODEL_FIELDS); #define KVDB_ENTRY_GET_RESULT_FIELDS(F) F(kvdbEntry, KvdbEntryInfo) JSON_STRUCT(KvdbEntryGetResult, KVDB_ENTRY_GET_RESULT_FIELDS); +#define KVDB_ENTRY_FIND_RESULT_FIELDS(F) F(kvdbEntry, std::optional) +JSON_STRUCT(KvdbEntryFindResult, KVDB_ENTRY_FIND_RESULT_FIELDS); + #define KVDB_ENTRY_SET_MODEL_FIELDS(F) \ F(kvdbId, std::string) \ F(kvdbEntryKey, std::string) \ diff --git a/endpoint/kvdb/include/privmx/endpoint/kvdb/varinterface/KvdbApiVarInterface.hpp b/endpoint/kvdb/include/privmx/endpoint/kvdb/varinterface/KvdbApiVarInterface.hpp index 1a6fc7e2..7ab91d66 100644 --- a/endpoint/kvdb/include/privmx/endpoint/kvdb/varinterface/KvdbApiVarInterface.hpp +++ b/endpoint/kvdb/include/privmx/endpoint/kvdb/varinterface/KvdbApiVarInterface.hpp @@ -46,6 +46,7 @@ class KvdbApiVarInterface { UnsubscribeFrom = 18, BuildSubscriptionQuery = 19, BuildSubscriptionQueryForSelectedEntry = 20, + FindEntry = 21, }; KvdbApiVarInterface(core::Connection connection, const core::VarSerializer& serializer) @@ -68,6 +69,7 @@ class KvdbApiVarInterface { Poco::Dynamic::Var unsubscribeFrom(const Poco::Dynamic::Var& args); Poco::Dynamic::Var buildSubscriptionQuery(const Poco::Dynamic::Var& args); Poco::Dynamic::Var buildSubscriptionQueryForSelectedEntry(const Poco::Dynamic::Var& args); + Poco::Dynamic::Var findEntry(const Poco::Dynamic::Var& args); Poco::Dynamic::Var exec(METHOD method, const Poco::Dynamic::Var& args); diff --git a/endpoint/kvdb/include_pub/privmx/endpoint/kvdb/KvdbApi.hpp b/endpoint/kvdb/include_pub/privmx/endpoint/kvdb/KvdbApi.hpp index 3864563d..f5fc00ba 100644 --- a/endpoint/kvdb/include_pub/privmx/endpoint/kvdb/KvdbApi.hpp +++ b/endpoint/kvdb/include_pub/privmx/endpoint/kvdb/KvdbApi.hpp @@ -119,6 +119,15 @@ class KvdbApi : public privmx::endpoint::core::ExtendedPointer { */ KvdbEntry getEntry(const std::string& kvdbId, const std::string& key); + /** + * Finds a KVDB entry by given KVDB entry key and KVDB ID, returning nullopt if not found. + * + * @param kvdbId KVDB ID of the KVDB entry to find + * @param key key of the KVDB entry to find + * @return optional struct containing the KVDB entry, or nullopt if not found + */ + std::optional findEntry(const std::string& kvdbId, const std::string& key); + /** * Check whether the KVDB entry exists. * diff --git a/endpoint/kvdb/src/KvdbApi.cpp b/endpoint/kvdb/src/KvdbApi.cpp index 298e28f2..2d1c9bd5 100644 --- a/endpoint/kvdb/src/KvdbApi.cpp +++ b/endpoint/kvdb/src/KvdbApi.cpp @@ -136,6 +136,17 @@ KvdbEntry KvdbApi::getEntry(const std::string& kvdbId, const std::string& key) { } } +std::optional KvdbApi::findEntry(const std::string& kvdbId, const std::string& key) { + auto impl = getImpl(); + core::Validator::validateId(kvdbId, "field:kvdbId "); + try { + return impl->findEntry(kvdbId, key); + } catch (const privmx::utils::PrivmxException& e) { + core::ExceptionConverter::rethrowAsCoreException(e); + throw core::Exception("ExceptionConverter rethrow error"); + } +} + bool KvdbApi::hasEntry(const std::string& kvdbId, const std::string& key) { auto impl = getImpl(); core::Validator::validateId(kvdbId, "field:kvdbId "); diff --git a/endpoint/kvdb/src/KvdbApiImpl.cpp b/endpoint/kvdb/src/KvdbApiImpl.cpp index eca08393..437b8f41 100644 --- a/endpoint/kvdb/src/KvdbApiImpl.cpp +++ b/endpoint/kvdb/src/KvdbApiImpl.cpp @@ -277,6 +277,18 @@ KvdbEntry KvdbApiImpl::getEntry(const std::string& kvdbId, const std::string& ke return result; } +std::optional KvdbApiImpl::findEntry(const std::string& kvdbId, const std::string& key) { + PRIVMX_DEBUG_TIME_START(PlatformKvdb, findEntry) + server::KvdbEntryGetModel model{.kvdbId = kvdbId, .kvdbEntryKey = key}; + PRIVMX_DEBUG_TIME_CHECKPOINT(PlatformKvdb, findEntry, getting entry) + auto entryOpt = _serverApi.kvdbEntryFind(model).kvdbEntry; + PRIVMX_DEBUG_TIME_STOP(PlatformKvdb, findEntry, data received) + if (!entryOpt.has_value()) { + return std::nullopt; + } + return validateDecryptAndConvertEntryDataToEntry(entryOpt.value(), getEntryDecryptionKeys(entryOpt.value())); +} + bool KvdbApiImpl::hasEntry(const std::string& kvdbId, const std::string& key) { try { server::KvdbEntryGetModel model{.kvdbId = kvdbId, .kvdbEntryKey = key}; diff --git a/endpoint/kvdb/src/ServerApi.cpp b/endpoint/kvdb/src/ServerApi.cpp index ef5bc342..e843fafd 100644 --- a/endpoint/kvdb/src/ServerApi.cpp +++ b/endpoint/kvdb/src/ServerApi.cpp @@ -45,6 +45,10 @@ server::KvdbEntryGetResult ServerApi::kvdbEntryGet(server::KvdbEntryGetModel mod return request("kvdbEntryGet", model.toJSON()); } +server::KvdbEntryFindResult ServerApi::kvdbEntryFind(server::KvdbEntryGetModel model) { + return request("kvdbEntryFind", model.toJSON()); +} + void ServerApi::kvdbEntrySet(server::KvdbEntrySetModel model) { request("kvdbEntrySet", model.toJSON()); } diff --git a/endpoint/kvdb/src/varinterface/KvdbApiVarInterface.cpp b/endpoint/kvdb/src/varinterface/KvdbApiVarInterface.cpp index 01181e0d..ba062566 100644 --- a/endpoint/kvdb/src/varinterface/KvdbApiVarInterface.cpp +++ b/endpoint/kvdb/src/varinterface/KvdbApiVarInterface.cpp @@ -35,7 +35,8 @@ std::map(argsArr->get(0), "kvdbId"); + auto key = _deserializer.deserialize(argsArr->get(1), "key"); + auto result = _kvdbApi.findEntry(kvdbId, key); + return _serializer.serialize(result); +} + Poco::Dynamic::Var KvdbApiVarInterface::exec(METHOD method, const Poco::Dynamic::Var& args) { auto it = methodMap.find(method); if (it == methodMap.end()) { diff --git a/endpoint/search/src/PrivmxFS.cpp b/endpoint/search/src/PrivmxFS.cpp index 9c33efa1..05d5fb16 100644 --- a/endpoint/search/src/PrivmxFS.cpp +++ b/endpoint/search/src/PrivmxFS.cpp @@ -152,7 +152,7 @@ std::shared_ptr PrivmxFS::openFile(const std::string& path) { bool PrivmxFS::access(const std::string& path) { LOG_TRACE("PrivmxFS::access - ", path, " | kvdbId: ",_session->kvdbId) - return _session->kvdbApi.hasEntry(_session->kvdbId, path); + return _session->kvdbApi.findEntry(_session->kvdbId, path).has_value(); } void PrivmxFS::deleteFile(const std::string& path) { @@ -172,20 +172,17 @@ PrivmxFS::PrivmxFS( std::string PrivmxFS::getFileId(const std::string& name) { LOG_TRACE("PrivmxFS::getFileId - ", name, " | kvdbId: ",_session->kvdbId) - try { - privmx::endpoint::kvdb::KvdbEntry kvdbEntry = _session->kvdbApi.getEntry(_session->kvdbId, name); - if(kvdbEntry.statusCode != 0) { - throw MalformedInternalFileIdException(); + auto entry = _session->kvdbApi.findEntry(_session->kvdbId, name); + if (entry.has_value()) { + if(entry->statusCode != 0) { + throw MalformedInternalFileIdException(); } - std::string fileId = kvdbEntry.data.stdString(); - return fileId; - } catch (const privmx::endpoint::server::KvdbEntryDoesNotExistException& e) { - LOG_DEBUG("PrivmxFS::getFileId file not found, creating new file - ", name) - int64_t fh = _session->storeApi.createFile(_session->storeId, META, META, 0, true); - std::string fileId = _session->storeApi.closeFile(fh); - _session->kvdbApi.setEntry(_session->kvdbId, name, META, META, privmx::endpoint::core::Buffer::from(fileId)); - return fileId; + return entry->data.stdString(); } + int64_t fh = _session->storeApi.createFile(_session->storeId, META, META, 0, true); + std::string fileId = _session->storeApi.closeFile(fh); + _session->kvdbApi.setEntry(_session->kvdbId, name, META, META, privmx::endpoint::core::Buffer::from(fileId)); + return fileId; } std::shared_ptr PrivmxExtFS::openFile(const std::string& path) {