changeset 966:886652370ff2

accelerating REST API matching
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 27 Jun 2014 15:33:22 +0200
parents d724ac031080
children dfc076546821
files Core/RestApi/RestApiPath.cpp Core/RestApi/RestApiPath.h Core/Toolbox.cpp Core/Toolbox.h UnitTestsSources/RestApi.cpp UnitTestsSources/UnitTestsMain.cpp
diffstat 6 files changed, 275 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/Core/RestApi/RestApiPath.cpp	Fri Jun 27 13:58:02 2014 +0200
+++ b/Core/RestApi/RestApiPath.cpp	Fri Jun 27 15:33:22 2014 +0200
@@ -33,6 +33,8 @@
 #include "../PrecompiledHeaders.h"
 #include "RestApiPath.h"
 
+#include "../OrthancException.h"
+
 #include <cassert>
 
 namespace Orthanc
@@ -89,6 +91,8 @@
                           UriComponents& trailing,
                           const UriComponents& uri) const
   {
+    assert(uri_.size() == components_.size());
+
     if (uri.size() < uri_.size())
     {
       return false;
@@ -135,4 +139,42 @@
     UriComponents trailing;
     return Match(components, trailing, uri);
   }
+
+
+  bool RestApiPath::IsWildcardLevel(size_t level) const
+  {
+    assert(uri_.size() == components_.size());
+
+    if (level >= uri_.size())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    return uri_[level].length() == 0;
+  }
+
+  const std::string& RestApiPath::GetWildcardName(size_t level) const
+  {
+    assert(uri_.size() == components_.size());
+
+    if (!IsWildcardLevel(level))
+    {
+      throw OrthancException(ErrorCode_BadParameterType);
+    }
+
+    return components_[level];
+  }
+
+  const std::string& RestApiPath::GetLevelName(size_t level) const
+  {
+    assert(uri_.size() == components_.size());
+
+    if (IsWildcardLevel(level))
+    {
+      throw OrthancException(ErrorCode_BadParameterType);
+    }
+
+    return uri_[level];
+  }
 }
+
--- a/Core/RestApi/RestApiPath.h	Fri Jun 27 13:58:02 2014 +0200
+++ b/Core/RestApi/RestApiPath.h	Fri Jun 27 15:33:22 2014 +0200
@@ -59,5 +59,22 @@
                const UriComponents& uri) const;
 
     bool Match(const UriComponents& uri) const;
+
+    size_t GetLevelCount() const
+    {
+      return uri_.size();
+    }
+
+    bool IsWildcardLevel(size_t level) const;
+
+    bool IsUniversalTrailing() const
+    {
+      return hasTrailing_;
+    }
+
+    const std::string& GetWildcardName(size_t level) const;
+
+    const std::string& GetLevelName(size_t level) const;
+
   };
 }
--- a/Core/Toolbox.cpp	Fri Jun 27 13:58:02 2014 +0200
+++ b/Core/Toolbox.cpp	Fri Jun 27 15:33:22 2014 +0200
@@ -284,6 +284,28 @@
   }
 
 
+  void Toolbox::TruncateUri(UriComponents& target,
+                            const UriComponents& source,
+                            size_t fromLevel)
+  {
+    target.clear();
+
+    if (source.size() > fromLevel)
+    {
+      target.resize(source.size() - fromLevel);
+
+      size_t j = 0;
+      for (size_t i = fromLevel; i < source.size(); i++, j++)
+      {
+        target[j] = source[i];
+      }
+
+      assert(j == target.size());
+    }
+  }
+  
+
+
   bool Toolbox::IsChildUri(const UriComponents& baseUri,
                            const UriComponents& testedUri)
   {
--- a/Core/Toolbox.h	Fri Jun 27 13:58:02 2014 +0200
+++ b/Core/Toolbox.h	Fri Jun 27 15:33:22 2014 +0200
@@ -73,6 +73,10 @@
     void SplitUriComponents(UriComponents& components,
                             const std::string& uri);
   
+    void TruncateUri(UriComponents& target,
+                     const UriComponents& source,
+                     size_t fromLevel);
+  
     bool IsChildUri(const UriComponents& baseUri,
                     const UriComponents& testedUri);
 
--- a/UnitTestsSources/RestApi.cpp	Fri Jun 27 13:58:02 2014 +0200
+++ b/UnitTestsSources/RestApi.cpp	Fri Jun 27 15:33:22 2014 +0200
@@ -138,6 +138,18 @@
     ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/"));
     ASSERT_FALSE(uri.Match(args, trail, "/a/moi/d"));
     ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi"));
+
+    ASSERT_EQ(3u, uri.GetLevelCount());
+    ASSERT_TRUE(uri.IsUniversalTrailing());
+
+    ASSERT_EQ("coucou", uri.GetLevelName(0));
+    ASSERT_THROW(uri.GetWildcardName(0), OrthancException);
+
+    ASSERT_EQ("abc", uri.GetWildcardName(1));
+    ASSERT_THROW(uri.GetLevelName(1), OrthancException);
+
+    ASSERT_EQ("d", uri.GetLevelName(2));
+    ASSERT_THROW(uri.GetWildcardName(2), OrthancException);
   }
 
   {
@@ -147,6 +159,18 @@
     ASSERT_EQ(1u, args.size());
     ASSERT_EQ(0u, trail.size());
     ASSERT_EQ("moi", args["abc"]);
+
+    ASSERT_EQ(3u, uri.GetLevelCount());
+    ASSERT_FALSE(uri.IsUniversalTrailing());
+
+    ASSERT_EQ("coucou", uri.GetLevelName(0));
+    ASSERT_THROW(uri.GetWildcardName(0), OrthancException);
+
+    ASSERT_EQ("abc", uri.GetWildcardName(1));
+    ASSERT_THROW(uri.GetLevelName(1), OrthancException);
+
+    ASSERT_EQ("d", uri.GetLevelName(2));
+    ASSERT_THROW(uri.GetWildcardName(2), OrthancException);
   }
 
   {
@@ -157,5 +181,139 @@
     ASSERT_EQ("a", trail[0]);
     ASSERT_EQ("b", trail[1]);
     ASSERT_EQ("c", trail[2]);
+
+    ASSERT_EQ(0u, uri.GetLevelCount());
+    ASSERT_TRUE(uri.IsUniversalTrailing());
   }
 }
+
+
+
+
+
+
+namespace Orthanc
+{
+  class RestApiResource
+  {
+  private:
+    struct Handlers
+    {
+      std::list<RestApi::GetHandler>  getHandlers_;
+      std::list<RestApi::PutHandler>  putHandlers_;
+      std::list<RestApi::PostHandler>  postHandlers_;
+      std::list<RestApi::DeleteHandler>  deleteHandlers_;
+
+      void Register(RestApi::GetHandler handler)
+      {
+        getHandlers_.push_back(handler);
+      }
+
+      void Register(RestApi::PutHandler handler)
+      {
+        putHandlers_.push_back(handler);
+      }
+
+      void Register(RestApi::PostHandler handler)
+      {
+        postHandlers_.push_back(handler);
+      }
+
+      void Register(RestApi::DeleteHandler handler)
+      {
+        deleteHandlers_.push_back(handler);
+      }
+    };
+
+
+    typedef std::map<std::string, RestApiResource*>  Children;
+
+    Children  children_;
+    Children  wildcardChildren_;
+    Handlers  handlers_;
+    Handlers  universalHandlers_;
+
+
+    static RestApiResource& AddChild(Children& children,
+                                     const std::string& name)
+    {
+      Children::iterator it = children.find(name);
+
+      if (it == children.end())
+      {
+        // Create new child
+        RestApiResource *child = new RestApiResource;
+        children[name] = child;
+        return *child;
+      }
+      else
+      {
+        return *it->second;
+      }
+    }
+
+
+    static void DeleteChildren(Children& children)
+    {
+      for (Children::iterator it = children.begin();
+           it != children.end(); it++)
+      {
+        delete it->second;
+      }
+    }
+
+
+
+
+    template <typename Handler>
+    void RegisterInternal(const RestApiPath& path,
+                          Handler handler,
+                          size_t level)
+    {
+      if (path.GetLevelCount() == level)
+      {
+        if (path.IsUniversalTrailing())
+        {
+          universalHandlers_.Register(handler);
+        }
+        else
+        {
+          handlers_.Register(handler);
+        }
+      }
+      else if (path.IsWildcardLevel(level))
+      {
+        AddChild(wildcardChildren_, path.GetWildcardName(level));
+      }
+    }
+
+
+  public:
+    ~RestApiResource()
+    {
+      DeleteChildren(children_);
+      DeleteChildren(wildcardChildren_);
+    }
+
+    void Register(const RestApiPath& path,
+                  RestApi::GetHandler handler)
+    {
+      RegisterInternal(path, handler, 0);
+    }
+  };
+
+}
+
+
+
+static void Toto(RestApi::GetCall& get)
+{
+}
+
+
+TEST(RestApi, RestApiResource)
+{
+  RestApiResource root;
+
+  root.Register(RestApiPath("/hello/world/test"), Toto);
+}
--- a/UnitTestsSources/UnitTestsMain.cpp	Fri Jun 27 13:58:02 2014 +0200
+++ b/UnitTestsSources/UnitTestsMain.cpp	Fri Jun 27 15:33:22 2014 +0200
@@ -212,7 +212,7 @@
 
 TEST(Uri, SplitUriComponents)
 {
-  UriComponents c;
+  UriComponents c, d;
   Toolbox::SplitUriComponents(c, "/cou/hello/world");
   ASSERT_EQ(3u, c.size());
   ASSERT_EQ("cou", c[0]);
@@ -253,6 +253,37 @@
 }
 
 
+TEST(Uri, Truncate)
+{
+  UriComponents c, d;
+  Toolbox::SplitUriComponents(c, "/cou/hello/world");
+
+  Toolbox::TruncateUri(d, c, 0);
+  ASSERT_EQ(3u, d.size());
+  ASSERT_EQ("cou", d[0]);
+  ASSERT_EQ("hello", d[1]);
+  ASSERT_EQ("world", d[2]);
+
+  Toolbox::TruncateUri(d, c, 1);
+  ASSERT_EQ(2u, d.size());
+  ASSERT_EQ("hello", d[0]);
+  ASSERT_EQ("world", d[1]);
+
+  Toolbox::TruncateUri(d, c, 2);
+  ASSERT_EQ(1u, d.size());
+  ASSERT_EQ("world", d[0]);
+
+  Toolbox::TruncateUri(d, c, 3);
+  ASSERT_EQ(0u, d.size());
+
+  Toolbox::TruncateUri(d, c, 4);
+  ASSERT_EQ(0u, d.size());
+
+  Toolbox::TruncateUri(d, c, 5);
+  ASSERT_EQ(0u, d.size());
+}
+
+
 TEST(Uri, Child)
 {
   UriComponents c1;  Toolbox::SplitUriComponents(c1, "/hello/world");