From b74756e38f7e92e0f7b5c98be4999e3bddc7cc36 Mon Sep 17 00:00:00 2001 From: Jojo-1000 <33495614+Jojo-1000@users.noreply.github.com> Date: Sat, 13 Mar 2021 20:51:09 +0100 Subject: [PATCH] Add methods to change the refresh duration individually. --- include/hueplusplus/APICache.h | 15 ++++++++++++++- include/hueplusplus/BaseDevice.h | 3 +++ include/hueplusplus/Bridge.h | 3 +++ include/hueplusplus/BridgeConfig.h | 3 +++ include/hueplusplus/Group.h | 3 +++ include/hueplusplus/ResourceList.h | 17 +++++++++++++---- include/hueplusplus/Rule.h | 3 +++ include/hueplusplus/Scene.h | 3 +++ include/hueplusplus/Schedule.h | 3 +++ src/APICache.cpp | 6 ++++++ src/BaseDevice.cpp | 5 +++++ src/Bridge.cpp | 6 ++++++ src/BridgeConfig.cpp | 6 ++++++ src/Group.cpp | 6 ++++++ src/Rule.cpp | 5 +++++ src/Scene.cpp | 5 +++++ src/Schedule.cpp | 6 ++++++ test/test_APICache.cpp | 45 +++++++++++++++++++++++++++++++++++---------- 18 files changed, 128 insertions(+), 15 deletions(-) diff --git a/include/hueplusplus/APICache.h b/include/hueplusplus/APICache.h index 038085c..34b1c41 100644 --- a/include/hueplusplus/APICache.h +++ b/include/hueplusplus/APICache.h @@ -30,6 +30,9 @@ namespace hueplusplus { +//! \brief Maximum duration, used to indicate that the cache should never be refreshed automatically. +constexpr std::chrono::steady_clock::duration c_refreshNever = std::chrono::steady_clock::duration::max(); + //! \brief Caches API GET requests and refreshes regularly. class APICache { @@ -49,7 +52,8 @@ public: //! \param commands HueCommandAPI for making API requests. //! \param refresh Interval between cache refreshing. May be 0 to always refresh. //! \param initial Initial value, may be null. If present, assumes the value is up to date. - APICache(const std::string& path, const HueCommandAPI& commands, std::chrono::steady_clock::duration refresh, const nlohmann::json& initial); + APICache(const std::string& path, const HueCommandAPI& commands, std::chrono::steady_clock::duration refresh, + const nlohmann::json& initial); //! \brief Refresh cache now. //! \throws std::system_error when system or socket operations fail @@ -70,6 +74,15 @@ public: //! \throws HueException when no previous request was cached const nlohmann::json& getValue() const; + //! \brief Set duration after which the cache is refreshed. + //! \param refreshDuration Interval between cache refreshing. + //! May be 0 to always refresh, or \ref c_refreshNever to never refresh. + //! + //! If the new refresh duration is exceeded, does not refresh immediately. + //! Instead, the next non-const getValue() call will refresh the value. + //! This is to reduce the number of unneccessary requests. + void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration); + //! \brief Get duration between refreshes. std::chrono::steady_clock::duration getRefreshDuration() const; diff --git a/include/hueplusplus/BaseDevice.h b/include/hueplusplus/BaseDevice.h index 1b7d979..09feed6 100644 --- a/include/hueplusplus/BaseDevice.h +++ b/include/hueplusplus/BaseDevice.h @@ -119,6 +119,9 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed virtual void refresh(bool force = false); + //! \brief Sets custom refresh interval for this device. + virtual void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration); + protected: BaseDevice(int id, const std::shared_ptr& baseCache); //! \brief Protected ctor that is used by subclasses. diff --git a/include/hueplusplus/Bridge.h b/include/hueplusplus/Bridge.h index 4232fe3..cb268c6 100644 --- a/include/hueplusplus/Bridge.h +++ b/include/hueplusplus/Bridge.h @@ -163,6 +163,9 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed void refresh(); + //! \brief Sets refresh interval for the whole bridge state. + void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration); + //! \brief Function to get the ip address of the hue bridge //! //! \return string containing ip diff --git a/include/hueplusplus/BridgeConfig.h b/include/hueplusplus/BridgeConfig.h index 0d70049..40227b7 100644 --- a/include/hueplusplus/BridgeConfig.h +++ b/include/hueplusplus/BridgeConfig.h @@ -67,6 +67,9 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed void refresh(bool force = false); + //! \brief Sets custom refresh interval for the config. + void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration); + //! \brief Get the list of whitelisted users //! \returns All users authorized for API access std::vector getWhitelistedUsers() const; diff --git a/include/hueplusplus/Group.h b/include/hueplusplus/Group.h index c6920ef..6b2eb5f 100644 --- a/include/hueplusplus/Group.h +++ b/include/hueplusplus/Group.h @@ -58,6 +58,9 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed void refresh(bool force = false); + //! \brief Sets custom refresh interval for this group. + void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration); + //! \name General information ///@{ diff --git a/include/hueplusplus/ResourceList.h b/include/hueplusplus/ResourceList.h index 4931f26..f97d1f9 100644 --- a/include/hueplusplus/ResourceList.h +++ b/include/hueplusplus/ResourceList.h @@ -40,7 +40,8 @@ namespace hueplusplus //! //! The resources are assumed to be in an object with ids as keys. //! The Resource class needs a constructor that accepts \c id, HueCommandAPI, \c refreshDuration and \c state; -//! otherwise a factory function needs to be provided that takes \c id and the JSON state. +//! otherwise a factory function needs to be provided that takes \c id, \c state +//! and a base cache that is null when shared state is disabled. template class ResourceList { @@ -57,6 +58,7 @@ public: //! \param baseCache Base cache which holds the parent state, not nullptr //! \param cacheEntry Entry name of the list state in the base cache //! \param refreshDuration Interval between refreshing the cache + //! \param sharedState Whether created resources should share the same base cache. //! \param factory Optional factory function to create Resources. //! Necessary if Resource is not constructible as described above. ResourceList(std::shared_ptr baseCache, const std::string& cacheEntry, @@ -92,6 +94,12 @@ public: //! \brief Refreshes internal state now void refresh() { stateCache->refresh(); } + //! \brief Sets custom refresh interval for this list and all resources created. + void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) + { + stateCache->setRefreshDuration(refreshDuration); + } + //! \brief Get all resources that exist //! \returns A vector of references to every Resource //! \throws std::system_error when system or socket operations fail @@ -173,8 +181,9 @@ protected: //! \throws HueException when factory is nullptr and Resource cannot be constructed as specified above. Resource construct(const IdType& id, const nlohmann::json& state) { - return construct( - id, state, std::is_constructible {}); + return construct(id, state, + std::is_constructible {}); } //! \brief Protected defaulted move constructor @@ -337,7 +346,7 @@ public: { throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Resource id is not valid"); } - return this->construct(id, id == 0 ? nlohmann::json{ nullptr } : state[key]); + return this->construct(id, id == 0 ? nlohmann::json {nullptr} : state[key]); } //! \brief Get group, specially handles group 0 //! \see ResourceList::exists diff --git a/include/hueplusplus/Rule.h b/include/hueplusplus/Rule.h index 4bd786a..157e9f3 100644 --- a/include/hueplusplus/Rule.h +++ b/include/hueplusplus/Rule.h @@ -58,6 +58,9 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed void refresh(bool force = false); + //! \brief Sets custom refresh interval for this rule. + void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration); + //! \brief Get rule identifier int getId() const; diff --git a/include/hueplusplus/Scene.h b/include/hueplusplus/Scene.h index 626d768..81185d6 100644 --- a/include/hueplusplus/Scene.h +++ b/include/hueplusplus/Scene.h @@ -139,6 +139,9 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed void refresh(bool force = false); + //! \brief Sets custom refresh interval for this group. + void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration); + //! \brief Get scene identifier std::string getId() const; //! \brief Get scene name diff --git a/include/hueplusplus/Schedule.h b/include/hueplusplus/Schedule.h index 6b2cc1b..6941629 100644 --- a/include/hueplusplus/Schedule.h +++ b/include/hueplusplus/Schedule.h @@ -48,6 +48,9 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed void refresh(); + //! \brief Sets custom refresh interval for this schedule. + void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration); + //! \brief Get schedule identifier int getId() const; diff --git a/src/APICache.cpp b/src/APICache.cpp index 1983a3d..9dbadf4 100644 --- a/src/APICache.cpp +++ b/src/APICache.cpp @@ -25,6 +25,7 @@ namespace hueplusplus { + APICache::APICache( std::shared_ptr baseCache, const std::string& subEntry, std::chrono::steady_clock::duration refresh) : base(baseCache), @@ -113,6 +114,11 @@ const nlohmann::json& APICache::getValue() const } } +void APICache::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) +{ + this->refreshDuration = refreshDuration; +} + std::chrono::steady_clock::duration APICache::getRefreshDuration() const { return refreshDuration; diff --git a/src/BaseDevice.cpp b/src/BaseDevice.cpp index 72b13d3..a7018c0 100644 --- a/src/BaseDevice.cpp +++ b/src/BaseDevice.cpp @@ -120,4 +120,9 @@ void BaseDevice::refresh(bool force) } } +void BaseDevice::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) +{ + state.setRefreshDuration(refreshDuration); +} + } // namespace hueplusplus diff --git a/src/Bridge.cpp b/src/Bridge.cpp index 6ef7f7b..f2db271 100644 --- a/src/Bridge.cpp +++ b/src/Bridge.cpp @@ -166,6 +166,12 @@ void Bridge::refresh() stateCache->refresh(); } +void Bridge::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) +{ + stateCache->setRefreshDuration(refreshDuration); +} + + std::string Bridge::getBridgeIP() const { return ip; diff --git a/src/BridgeConfig.cpp b/src/BridgeConfig.cpp index 0953e6d..6073d41 100644 --- a/src/BridgeConfig.cpp +++ b/src/BridgeConfig.cpp @@ -39,6 +39,12 @@ void BridgeConfig::refresh(bool force) cache.getValue(); } } + +void BridgeConfig::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) +{ + cache.setRefreshDuration(refreshDuration); +} + std::vector BridgeConfig::getWhitelistedUsers() const { const nlohmann::json& whitelist = cache.getValue().at("whitelist"); diff --git a/src/Group.cpp b/src/Group.cpp index 9847e6e..74c50b6 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -27,6 +27,12 @@ void Group::refresh(bool force) } } +void Group::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) +{ + state.setRefreshDuration(refreshDuration); +} + + int Group::getId() const { return id; diff --git a/src/Rule.cpp b/src/Rule.cpp index f81e318..8d85aaa 100644 --- a/src/Rule.cpp +++ b/src/Rule.cpp @@ -148,6 +148,11 @@ void Rule::refresh(bool force) } } +void Rule::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) +{ + state.setRefreshDuration(refreshDuration); +} + int Rule::getId() const { return id; diff --git a/src/Scene.cpp b/src/Scene.cpp index c5fac14..5be95d6 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -168,6 +168,11 @@ void Scene::refresh(bool force) } } +void Scene::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) +{ + state.setRefreshDuration(refreshDuration); +} + std::string Scene::getId() const { return id; diff --git a/src/Schedule.cpp b/src/Schedule.cpp index 888a281..1ec862a 100644 --- a/src/Schedule.cpp +++ b/src/Schedule.cpp @@ -35,6 +35,12 @@ void Schedule::refresh() state.refresh(); } +void Schedule::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration) +{ + state.setRefreshDuration(refreshDuration); +} + + int Schedule::getId() const { return id; diff --git a/test/test_APICache.cpp b/test/test_APICache.cpp index b6ef77d..f0dc657 100644 --- a/test/test_APICache.cpp +++ b/test/test_APICache.cpp @@ -44,7 +44,7 @@ TEST(APICache, getRefreshDuration) EXPECT_EQ(refresh, cache.getRefreshDuration()); } { - std::chrono::steady_clock::duration refresh = std::chrono::steady_clock::duration::max(); + std::chrono::steady_clock::duration refresh = c_refreshNever; APICache cache("", commands, refresh, nullptr); EXPECT_EQ(refresh, cache.getRefreshDuration()); } @@ -95,7 +95,7 @@ TEST(APICache, refreshBase) // Base cache with max duration { auto baseCache - = std::make_shared(basePath, commands, std::chrono::steady_clock::duration::max(), nullptr); + = std::make_shared(basePath, commands, c_refreshNever, nullptr); APICache cache(baseCache, "abc", std::chrono::seconds(0)); // First call refreshes base, second call only child @@ -149,7 +149,7 @@ TEST(APICache, getValue) // Only refresh once { std::string path = "/test/abc"; - APICache cache(path, commands, std::chrono::steady_clock::duration::max(), nullptr); + APICache cache(path, commands, c_refreshNever, nullptr); nlohmann::json value = {{"a", "b"}}; EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) @@ -173,7 +173,7 @@ TEST(APICache, getValue) // No refresh with const throws exception { std::string path = "/test/abc"; - const APICache cache(path, commands, std::chrono::steady_clock::duration::max(), nullptr); + const APICache cache(path, commands, c_refreshNever, nullptr); nlohmann::json value = {{"a", "b"}}; EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) @@ -185,7 +185,7 @@ TEST(APICache, getValue) { std::string path = "/test/abc"; nlohmann::json value = {{"a", "b"}}; - APICache cache(path, commands, std::chrono::steady_clock::duration::max(), value); + APICache cache(path, commands, c_refreshNever, value); EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) .Times(0); @@ -197,7 +197,7 @@ TEST(APICache, getValue) { std::string path = "/test/abc"; nlohmann::json value = {{"a", "b"}}; - const APICache cache(path, commands, std::chrono::steady_clock::duration::max(), value); + const APICache cache(path, commands, c_refreshNever, value); EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) .Times(0); @@ -231,7 +231,7 @@ TEST(APICache, getValueBase) // Child duration > base duration { auto baseCache = std::make_shared(basePath, commands, std::chrono::seconds(0), nullptr); - APICache cache(baseCache, "abc", std::chrono::steady_clock::duration::max()); + APICache cache(baseCache, "abc", c_refreshNever); EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + basePath, nlohmann::json::object(), getBridgeIp(), getBridgePort())) .Times(1) @@ -243,7 +243,7 @@ TEST(APICache, getValueBase) // Child duration < base duration { auto baseCache - = std::make_shared(basePath, commands, std::chrono::steady_clock::duration::max(), nullptr); + = std::make_shared(basePath, commands, c_refreshNever, nullptr); APICache cache(baseCache, "abc", std::chrono::seconds(0)); const nlohmann::json updateChildValue = {{"test", "updated"}}; InSequence s; @@ -265,8 +265,8 @@ TEST(APICache, getValueBase) // Only refresh once { auto baseCache - = std::make_shared(basePath, commands, std::chrono::steady_clock::duration::max(), nullptr); - APICache cache(baseCache, "abc", std::chrono::steady_clock::duration::max()); + = std::make_shared(basePath, commands, c_refreshNever, nullptr); + APICache cache(baseCache, "abc", c_refreshNever); EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + basePath, nlohmann::json::object(), getBridgeIp(), getBridgePort())) .Times(1) @@ -277,6 +277,31 @@ TEST(APICache, getValueBase) } } + +TEST(APICache, setRefreshDuration) +{ + using namespace ::testing; + auto handler = std::make_shared(); + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + { + std::string path = "/test/abc"; + APICache cache(path, commands, std::chrono::seconds(0), nullptr); + nlohmann::json value = { {"a", "b"} }; + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(Return(value)); + EXPECT_EQ(value, cache.getValue()); + cache.setRefreshDuration(c_refreshNever); + EXPECT_EQ(c_refreshNever, cache.getRefreshDuration()); + // Next getValue does not refresh + EXPECT_EQ(value, cache.getValue()); + Mock::VerifyAndClearExpectations(handler.get()); + } +} + + TEST(APICache, getRequestPath) { using namespace ::testing; -- libgit2 0.21.4