# HG changeset patch # User Sebastien Jodogne # Date 1403884135 -7200 # Node ID 8ed284e798507b730c74d259350150e244511009 # Parent dfc076546821be733922a4cde911b1b773baee40 RestApiHierarchy diff -r dfc076546821 -r 8ed284e79850 UnitTestsSources/RestApiTests.cpp --- a/UnitTestsSources/RestApiTests.cpp Fri Jun 27 15:36:38 2014 +0200 +++ b/UnitTestsSources/RestApiTests.cpp Fri Jun 27 17:48:55 2014 +0200 @@ -194,15 +194,25 @@ namespace Orthanc { - class RestApiResource + class RestApiHierarchy { private: struct Handlers { - std::list getHandlers_; - std::list putHandlers_; - std::list postHandlers_; - std::list deleteHandlers_; + typedef std::list GetHandlers; + typedef std::list PostHandlers; + typedef std::list PutHandlers; + typedef std::list DeleteHandlers; + + GetHandlers getHandlers_; + PostHandlers postHandlers_; + PutHandlers putHandlers_; + DeleteHandlers deleteHandlers_; + + bool HasGet() const + { + return getHandlers_.size() > 0; + } void Register(RestApi::GetHandler handler) { @@ -223,26 +233,39 @@ { deleteHandlers_.push_back(handler); } + + bool IsEmpty() const + { + return (getHandlers_.empty() && + postHandlers_.empty() && + putHandlers_.empty() && + deleteHandlers_.empty()); + } }; - typedef std::map Children; + typedef std::map Children; + typedef bool (*ResourceCallback) (Handlers&, + const UriComponents& uri, + const RestApiPath::Components& components, + const UriComponents& trailing, + void* call); + Handlers handlers_; Children children_; Children wildcardChildren_; - Handlers handlers_; Handlers universalHandlers_; - static RestApiResource& AddChild(Children& children, - const std::string& name) + static RestApiHierarchy& AddChild(Children& children, + const std::string& name) { Children::iterator it = children.find(name); if (it == children.end()) { // Create new child - RestApiResource *child = new RestApiResource; + RestApiHierarchy *child = new RestApiHierarchy; children[name] = child; return *child; } @@ -263,8 +286,6 @@ } - - template void RegisterInternal(const RestApiPath& path, Handler handler, @@ -281,15 +302,220 @@ handlers_.Register(handler); } } - else if (path.IsWildcardLevel(level)) + else { - AddChild(wildcardChildren_, path.GetWildcardName(level)); + RestApiHierarchy* child; + if (path.IsWildcardLevel(level)) + { + child = &AddChild(wildcardChildren_, path.GetWildcardName(level)); + } + else + { + child = &AddChild(children_, path.GetLevelName(level)); + } + + child->RegisterInternal(path, handler, level + 1); } } + bool LookupHandler(RestApiPath::Components& components, + const UriComponents& uri, + ResourceCallback callback, + size_t level, + void* call) + { + assert(uri.size() >= level); + UriComponents trailing; + + // Look for an exact match on the resource of interest + if (uri.size() == level) + { + if (!handlers_.IsEmpty() && + callback(handlers_, uri, components, trailing, call)) + { + return true; + } + } + + + // Try and go down in the hierarchy, using an exact match for the child + Children::const_iterator child = children_.find(uri[level]); + if (child != children_.end()) + { + if (child->second->LookupHandler(components, uri, callback, level + 1, call)) + { + return true; + } + } + + + // Try and go down in the hierarchy, using wildcard rules for children + for (child = wildcardChildren_.begin(); + child != wildcardChildren_.end(); child++) + { + RestApiPath::Components subComponents = components; + subComponents[child->first] = uri[level]; + + if (child->second->LookupHandler(components, uri, callback, level + 1, call)) + { + return true; + } + } + + + // As a last resort, call the universal handlers, if any + if (!universalHandlers_.IsEmpty()) + { + trailing.resize(uri.size() - level); + size_t pos = 0; + for (size_t i = level; i < uri.size(); i++, pos++) + { + trailing[pos] = uri[i]; + } + + assert(pos == trailing.size()); + + if (callback(universalHandlers_, uri, components, trailing, call)) + { + return true; + } + } + + return false; + } + + + bool GetDirectory(Json::Value& result, + const UriComponents& uri, + size_t level) + { + if (uri.size() == level) + { + if (!handlers_.HasGet() && + universalHandlers_.IsEmpty() && + wildcardChildren_.size() == 0) + { + result = Json::arrayValue; + + for (Children::const_iterator it = children_.begin(); + it != children_.end(); it++) + { + result.append(it->first); + } + + return true; + } + else + { + return false; + } + } + + Children::const_iterator child = children_.find(uri[level]); + if (child != children_.end()) + { + if (child->second->GetDirectory(result, uri, level + 1)) + { + return true; + } + } + + for (child = wildcardChildren_.begin(); + child != wildcardChildren_.end(); child++) + { + if (child->second->GetDirectory(result, uri, level + 1)) + { + return true; + } + } + + return false; + } + + + static bool GetCallback(Handlers& handlers, + const UriComponents& uri, + const RestApiPath::Components& components, + const UriComponents& trailing, + void* call) + { + for (Handlers::GetHandlers::iterator + it = handlers.getHandlers_.begin(); + it != handlers.getHandlers_.end(); it++) + { + // TODO RETURN BOOL + + (*it) (*reinterpret_cast(call)); + return true; + } + + return false; + } + + + static bool PostCallback(Handlers& handlers, + const UriComponents& uri, + const RestApiPath::Components& components, + const UriComponents& trailing, + void* call) + { + for (Handlers::PostHandlers::iterator + it = handlers.postHandlers_.begin(); + it != handlers.postHandlers_.end(); it++) + { + // TODO RETURN BOOL + + (*it) (*reinterpret_cast(call)); + return true; + } + + return false; + } + + + static bool PutCallback(Handlers& handlers, + const UriComponents& uri, + const RestApiPath::Components& components, + const UriComponents& trailing, + void* call) + { + for (Handlers::PutHandlers::iterator + it = handlers.putHandlers_.begin(); + it != handlers.putHandlers_.end(); it++) + { + // TODO RETURN BOOL + + (*it) (*reinterpret_cast(call)); + return true; + } + + return false; + } + + + static bool DeleteCallback(Handlers& handlers, + const UriComponents& uri, + const RestApiPath::Components& components, + const UriComponents& trailing, + void* call) + { + for (Handlers::DeleteHandlers::iterator + it = handlers.deleteHandlers_.begin(); + it != handlers.deleteHandlers_.end(); it++) + { + // TODO RETURN BOOL + + (*it) (*reinterpret_cast(call)); + return true; + } + + return false; + } + + public: - ~RestApiResource() + ~RestApiHierarchy() { DeleteChildren(children_); DeleteChildren(wildcardChildren_); @@ -300,20 +526,174 @@ { RegisterInternal(path, handler, 0); } + + void Register(const RestApiPath& path, + RestApi::PutHandler handler) + { + RegisterInternal(path, handler, 0); + } + + void Register(const RestApiPath& path, + RestApi::PostHandler handler) + { + RegisterInternal(path, handler, 0); + } + + void Register(const RestApiPath& path, + RestApi::DeleteHandler handler) + { + RegisterInternal(path, handler, 0); + } + + void CreateSiteMap(Json::Value& target) const + { + if (children_.size() == 0) + { + std::string s = " "; + if (handlers_.getHandlers_.size() != 0) + { + s += "GET "; + } + + if (handlers_.postHandlers_.size() != 0) + { + s += "POST "; + } + + if (handlers_.putHandlers_.size() != 0) + { + s += "PUT "; + } + + if (handlers_.deleteHandlers_.size() != 0) + { + s += "DELETE "; + } + + target = s; + } + else + { + target = Json::objectValue; + + for (Children::const_iterator it = children_.begin(); + it != children_.end(); it++) + { + it->second->CreateSiteMap(target[it->first]); + } + } + + /*for (Children::const_iterator it = wildcardChildren_.begin(); + it != wildcardChildren_.end(); it++) + { + it->second->CreateSiteMap(target["* (" + it->first + ")"]); + }*/ + } + + bool GetDirectory(Json::Value& result, + const UriComponents& uri) + { + return GetDirectory(result, uri, 0); + } + + bool GetDirectory(Json::Value& result, + const std::string& uri) + { + UriComponents c; + Toolbox::SplitUriComponents(c, uri); + return GetDirectory(result, c, 0); + } + + bool Handle(RestApi::GetCall& call, + const UriComponents& uri) + { + RestApiPath::Components components; + return LookupHandler(components, uri, GetCallback, 0, &call); + } + + bool Handle(RestApi::PutCall& call, + const UriComponents& uri) + { + RestApiPath::Components components; + return LookupHandler(components, uri, PutCallback, 0, &call); + } + + bool Handle(RestApi::PostCall& call, + const UriComponents& uri) + { + RestApiPath::Components components; + return LookupHandler(components, uri, PostCallback, 0, &call); + } + + bool Handle(RestApi::DeleteCall& call, + const UriComponents& uri) + { + RestApiPath::Components components; + return LookupHandler(components, uri, DeleteCallback, 0, &call); + } + + bool Handle(RestApi::GetCall& call, + const std::string& uri) + { + UriComponents c; + Toolbox::SplitUriComponents(c, uri); + return Handle(call, c); + } }; } -static void Toto(RestApi::GetCall& get) + +static int testValue; + +template +static void SetValue(RestApi::GetCall& get) { + testValue = value; } -TEST(RestApi, RestApiResource) +TEST(RestApi, RestApiHierarchy) { - RestApiResource root; + RestApiHierarchy root; + root.Register(RestApiPath("/hello/world/test"), SetValue<1>); + root.Register(RestApiPath("/hello/world/test2"), SetValue<2>); + root.Register(RestApiPath("/hello/{world}/test3/test4"), SetValue<3>); + root.Register(RestApiPath("/hello2/*"), SetValue<4>); + + Json::Value m; + root.CreateSiteMap(m); + std::cout << m; + + Json::Value d; + ASSERT_FALSE(root.GetDirectory(d, "/hello")); + + ASSERT_TRUE(root.GetDirectory(d, "/hello/a")); + ASSERT_EQ(1u, d.size()); + ASSERT_EQ("test3", d[0].asString()); + + ASSERT_TRUE(root.GetDirectory(d, "/hello/world")); + ASSERT_EQ(2u, d.size()); - root.Register(RestApiPath("/hello/world/test"), Toto); + ASSERT_TRUE(root.GetDirectory(d, "/hello/a/test3")); + ASSERT_EQ(1u, d.size()); + ASSERT_EQ("test4", d[0].asString()); + + ASSERT_FALSE(root.GetDirectory(d, "/hello/world/test")); + ASSERT_FALSE(root.GetDirectory(d, "/hello/world/test2")); + ASSERT_FALSE(root.GetDirectory(d, "/hello2")); + + testValue = 0; + ASSERT_TRUE(root.Handle(*reinterpret_cast(NULL), "/hello/world/test")); + ASSERT_EQ(testValue, 1); + ASSERT_TRUE(root.Handle(*reinterpret_cast(NULL), "/hello/world/test2")); + ASSERT_EQ(testValue, 2); + ASSERT_TRUE(root.Handle(*reinterpret_cast(NULL), "/hello/b/test3/test4")); + ASSERT_EQ(testValue, 3); + ASSERT_FALSE(root.Handle(*reinterpret_cast(NULL), "/hello/b/test3/test")); + ASSERT_EQ(testValue, 3); + ASSERT_TRUE(root.Handle(*reinterpret_cast(NULL), "/hello2/a/b")); + ASSERT_EQ(testValue, 4); }