changeset 2823:807169f85ba9

OrthancPluginGetPeerUserProperty()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Sep 2018 15:38:18 +0200
parents a0b729ac0549
children 0e1b79bc4a2d
files Core/HttpClient.cpp Core/WebServiceParameters.cpp Core/WebServiceParameters.h Plugins/Engine/OrthancPlugins.cpp Plugins/Include/orthanc/OrthancCPlugin.h Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Plugins/Samples/Common/OrthancPluginCppWrapper.h UnitTestsSources/RestApiTests.cpp
diffstat 8 files changed, 290 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/Core/HttpClient.cpp	Mon Sep 17 12:08:49 2018 +0200
+++ b/Core/HttpClient.cpp	Tue Sep 18 15:38:18 2018 +0200
@@ -401,7 +401,7 @@
 
     SetUrl(service.GetUrl() + uri);
 
-    for (WebServiceParameters::HttpHeaders::const_iterator 
+    for (WebServiceParameters::Dictionary::const_iterator 
            it = service.GetHttpHeaders().begin();
          it != service.GetHttpHeaders().end(); ++it)
     {
--- a/Core/WebServiceParameters.cpp	Mon Sep 17 12:08:49 2018 +0200
+++ b/Core/WebServiceParameters.cpp	Tue Sep 18 15:38:18 2018 +0200
@@ -58,6 +58,20 @@
   static const char* KEY_USERNAME = "Username";
 
 
+  static bool IsReservedKey(const std::string& key)
+  {
+    return (key == KEY_CERTIFICATE_FILE ||
+            key == KEY_CERTIFICATE_KEY_FILE ||
+            key == KEY_CERTIFICATE_KEY_PASSWORD ||
+            key == KEY_HTTP_HEADERS ||
+            key == KEY_PASSWORD ||
+            key == KEY_PKCS11 ||
+            key == KEY_URL ||
+            key == KEY_URL_2 ||
+            key == KEY_USERNAME);
+  }
+
+
   WebServiceParameters::WebServiceParameters() : 
     pkcs11Enabled_(false)
   {
@@ -237,6 +251,7 @@
       pkcs11Enabled_ = false;
     }
 
+
     headers_.clear();
 
     if (peer.isMember(KEY_HTTP_HEADERS))
@@ -263,6 +278,27 @@
         }
       }
     }
+
+
+    userProperties_.clear();
+
+    const Json::Value::Members members = peer.getMemberNames();
+
+    for (Json::Value::Members::const_iterator it = members.begin(); 
+         it != members.end(); ++it)
+    {
+      if (!IsReservedKey(*it))
+      {
+        if (peer[*it].type() != Json::stringValue)
+        {
+          throw OrthancException(ErrorCode_BadFileFormat);
+        }
+        else
+        {
+          userProperties_[*it] = peer[*it].asString();
+        }
+      }
+    }
   }
 
 
@@ -298,7 +334,7 @@
   {
     target.clear();
 
-    for (HttpHeaders::const_iterator it = headers_.begin();
+    for (Dictionary::const_iterator it = headers_.begin();
          it != headers_.end(); ++it)
     {
       target.insert(it->first);
@@ -309,7 +345,7 @@
   bool WebServiceParameters::LookupHttpHeader(std::string& value,
                                               const std::string& key) const
   {
-    HttpHeaders::const_iterator found = headers_.find(key);
+    Dictionary::const_iterator found = headers_.find(key);
 
     if (found == headers_.end())
     {
@@ -323,13 +359,58 @@
   }
 
 
+  void WebServiceParameters::AddUserProperty(const std::string& key,
+                                             const std::string& value)
+  {
+    if (IsReservedKey(key))
+    {
+      LOG(ERROR) << "Cannot use this reserved key to name an user property: " << key;
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      userProperties_[key] = value;
+    }
+  }
+
+
+  void WebServiceParameters::ListUserProperties(std::set<std::string>& target) const
+  {
+    target.clear();
+
+    for (Dictionary::const_iterator it = userProperties_.begin();
+         it != userProperties_.end(); ++it)
+    {
+      target.insert(it->first);
+    }
+  }
+
+
+  bool WebServiceParameters::LookupUserProperty(std::string& value,
+                                                const std::string& key) const
+  {
+    Dictionary::const_iterator found = userProperties_.find(key);
+
+    if (found == userProperties_.end())
+    {
+      return false;
+    }
+    else
+    {
+      value = found->second;
+      return true;
+    }
+  }
+
+
   bool WebServiceParameters::IsAdvancedFormatNeeded() const
   {
     return (!certificateFile_.empty() ||
             !certificateKeyFile_.empty() ||
             !certificateKeyPassword_.empty() ||
             pkcs11Enabled_ ||
-            !headers_.empty());
+            !headers_.empty() ||
+            !userProperties_.empty());
   }
 
 
@@ -373,11 +454,17 @@
       value[KEY_PKCS11] = pkcs11Enabled_;
 
       value[KEY_HTTP_HEADERS] = Json::objectValue;
-      for (HttpHeaders::const_iterator it = headers_.begin();
+      for (Dictionary::const_iterator it = headers_.begin();
            it != headers_.end(); ++it)
       {
         value[KEY_HTTP_HEADERS][it->first] = it->second;
       }
+
+      for (Dictionary::const_iterator it = userProperties_.begin();
+           it != userProperties_.end(); ++it)
+      {
+        value[it->first] = it->second;
+      }
     }
     else
     {
--- a/Core/WebServiceParameters.h	Mon Sep 17 12:08:49 2018 +0200
+++ b/Core/WebServiceParameters.h	Tue Sep 18 15:38:18 2018 +0200
@@ -47,7 +47,7 @@
   class WebServiceParameters
   {
   public:
-    typedef std::map<std::string, std::string>  HttpHeaders;
+    typedef std::map<std::string, std::string>  Dictionary;
 
   private:
     std::string  url_;
@@ -57,7 +57,8 @@
     std::string  certificateKeyFile_;
     std::string  certificateKeyPassword_;
     bool         pkcs11Enabled_;
-    HttpHeaders  headers_;
+    Dictionary   headers_;
+    Dictionary   userProperties_;
 
     void FromSimpleFormat(const Json::Value& peer);
 
@@ -135,7 +136,7 @@
       headers_.clear();
     }
 
-    const HttpHeaders& GetHttpHeaders() const
+    const Dictionary& GetHttpHeaders() const
     {
       return headers_;
     }
@@ -145,6 +146,24 @@
     bool LookupHttpHeader(std::string& value,
                           const std::string& key) const; 
 
+    void AddUserProperty(const std::string& key,
+                         const std::string& value);
+
+    void ClearUserProperties()
+    {
+      userProperties_.clear();
+    }
+
+    const Dictionary& GetUserProperties() const
+    {
+      return userProperties_;
+    }
+
+    void ListUserProperties(std::set<std::string>& target) const; 
+
+    bool LookupUserProperty(std::string& value,
+                            const std::string& key) const; 
+
     bool IsAdvancedFormatNeeded() const;
 
     void Unserialize(const Json::Value& peer);
--- a/Plugins/Engine/OrthancPlugins.cpp	Mon Sep 17 12:08:49 2018 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Tue Sep 18 15:38:18 2018 +0200
@@ -2872,6 +2872,37 @@
         }
       }
 
+      case _OrthancPluginService_GetPeerUserProperty:
+      {
+        const _OrthancPluginGetPeerProperty& p =
+          *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
+
+        if (p.peers == NULL ||
+            p.userProperty == NULL)
+        {
+          throw OrthancException(ErrorCode_NullPointer);
+        }
+        else
+        {
+          const WebServiceParameters::Dictionary& properties = 
+            reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUserProperties();
+
+          WebServiceParameters::Dictionary::const_iterator found =
+            properties.find(p.userProperty);
+
+          if (found == properties.end())
+          {
+            *(p.target) = NULL;
+          }
+          else
+          {
+            *(p.target) = found->second.c_str();
+          }
+
+          return true;
+        }
+      }
+
       case _OrthancPluginService_CallPeerApi:
         CallPeerApi(parameters);
         return true;
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Sep 17 12:08:49 2018 +0200
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Tue Sep 18 15:38:18 2018 +0200
@@ -527,6 +527,7 @@
     _OrthancPluginService_GetPeerName = 8004,
     _OrthancPluginService_GetPeerUrl = 8005,
     _OrthancPluginService_CallPeerApi = 8006,
+    _OrthancPluginService_GetPeerUserProperty = 8007,
 
     /* Primitives for handling jobs (new in 1.4.2) */
     _OrthancPluginService_CreateJob = 9000,
@@ -6000,6 +6001,7 @@
     const char**               target;
     const OrthancPluginPeers*  peers;
     uint32_t                   peerIndex;
+    const char*                userProperty;
   } _OrthancPluginGetPeerProperty;
 
   /**
@@ -6031,6 +6033,7 @@
     params.target = &target;
     params.peers = peers;
     params.peerIndex = peerIndex;
+    params.userProperty = NULL;
 
     if (context->InvokeService(context, _OrthancPluginService_GetPeerName, &params) != OrthancPluginErrorCode_Success)
     {
@@ -6071,6 +6074,7 @@
     params.target = &target;
     params.peers = peers;
     params.peerIndex = peerIndex;
+    params.userProperty = NULL;
 
     if (context->InvokeService(context, _OrthancPluginService_GetPeerUrl, &params) != OrthancPluginErrorCode_Success)
     {
@@ -6085,6 +6089,53 @@
 
 
 
+  /**
+   * @brief Get some user-defined property of an Orthanc peer.
+   *
+   * This function returns some user-defined property of some Orthanc
+   * peer. An user-defined property is a property that is associated
+   * with the peer in the Orthanc configuration file, but that is not
+   * recognized by the Orthanc core.
+   *
+   * This function is thread-safe: Several threads sharing the same
+   * OrthancPluginPeers object can simultaneously call this function.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param peers The data structure describing the Orthanc peers.
+   * @param peerIndex The index of the peer of interest.
+   * This value must be lower than OrthancPluginGetPeersCount().
+   * @param userProperty The user property of interest.
+   * @result The value of the user property, or NULL if it is not defined.
+   * @ingroup Toolbox
+   **/
+  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetPeerUserProperty(
+    OrthancPluginContext*      context,
+    const OrthancPluginPeers*  peers,
+    uint32_t                   peerIndex,
+    const char*                userProperty)
+  {
+    const char* target = NULL;
+
+    _OrthancPluginGetPeerProperty params;
+    memset(&params, 0, sizeof(params));
+    params.target = &target;
+    params.peers = peers;
+    params.peerIndex = peerIndex;
+    params.userProperty = userProperty;
+
+    if (context->InvokeService(context, _OrthancPluginService_GetPeerUserProperty, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* No such user property */
+      return NULL;
+    }
+    else
+    {
+      return target;
+    }
+  }
+
+
+
   typedef struct
   {
     OrthancPluginMemoryBuffer*  answerBody;
--- a/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Mon Sep 17 12:08:49 2018 +0200
+++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Tue Sep 18 15:38:18 2018 +0200
@@ -1288,6 +1288,22 @@
 
 
 #if HAS_ORTHANC_PLUGIN_PEERS == 1
+  size_t OrthancPeers::GetPeerIndex(const std::string& name) const
+  {
+    size_t index;
+    if (LookupName(index, name))
+    {
+      return index;
+    }
+    else
+    {
+      std::string s = "Inexistent peer: " + name;
+      OrthancPluginLogError(context_, s.c_str());
+      ORTHANC_PLUGINS_THROW_EXCEPTION(UnknownResource);
+    }
+  }
+
+
   OrthancPeers::OrthancPeers(OrthancPluginContext* context) :
     context_(context),
     peers_(NULL),
@@ -1391,20 +1407,42 @@
   
   std::string OrthancPeers::GetPeerUrl(const std::string& name) const
   {
-    size_t index;
-    if (LookupName(index, name))
+    return GetPeerUrl(GetPeerIndex(name));
+  }
+
+
+  bool OrthancPeers::LookupUserProperty(std::string& value,
+                                        size_t index,
+                                        const std::string& key) const
+  {
+    if (index >= index_.size())
     {
-      return GetPeerUrl(index);
+      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
     else
     {
-      std::string s = "Inexistent peer: " + name;
-      OrthancPluginLogError(context_, s.c_str());
-      ORTHANC_PLUGINS_THROW_EXCEPTION(UnknownResource);
+      const char* s = OrthancPluginGetPeerUserProperty(context_, peers_, static_cast<uint32_t>(index), key.c_str());
+      if (s == NULL)
+      {
+        return false;
+      }
+      else
+      {
+        value.assign(s);
+        return true;
+      }
     }
   }
 
 
+  bool OrthancPeers::LookupUserProperty(std::string& value,
+                                        const std::string& peer,
+                                        const std::string& key) const
+  {
+    return LookupUserProperty(value, GetPeerIndex(peer), key);
+  }
+
+
   bool OrthancPeers::DoGet(MemoryBuffer& target,
                            size_t index,
                            const std::string& uri) const
--- a/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Mon Sep 17 12:08:49 2018 +0200
+++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Tue Sep 18 15:38:18 2018 +0200
@@ -553,6 +553,8 @@
     Index                 index_;
     uint32_t              timeout_;
 
+    size_t GetPeerIndex(const std::string& name) const;
+
   public:
     OrthancPeers(OrthancPluginContext* context);
 
@@ -582,6 +584,14 @@
       return index_.size();
     }
 
+    bool LookupUserProperty(std::string& value,
+                            size_t index,
+                            const std::string& key) const;
+
+    bool LookupUserProperty(std::string& value,
+                            const std::string& peer,
+                            const std::string& key) const;
+
     bool DoGet(MemoryBuffer& target,
                size_t index,
                const std::string& uri) const;
--- a/UnitTestsSources/RestApiTests.cpp	Mon Sep 17 12:08:49 2018 +0200
+++ b/UnitTestsSources/RestApiTests.cpp	Tue Sep 18 15:38:18 2018 +0200
@@ -603,3 +603,43 @@
     ASSERT_FALSE(p2.LookupHttpHeader(s, "nope"));
   }
 }
+
+
+TEST(WebServiceParameters, UserProperties)
+{
+  Json::Value v = Json::nullValue;
+
+  {
+    WebServiceParameters p;
+    p.SetUrl("http://localhost:8042/");
+    ASSERT_FALSE(p.IsAdvancedFormatNeeded());
+
+    ASSERT_THROW(p.AddUserProperty("Url", "nope"), OrthancException);
+    p.AddUserProperty("Hello", "world");
+    p.AddUserProperty("a", "b");
+    ASSERT_TRUE(p.IsAdvancedFormatNeeded());
+
+    p.Serialize(v, false, true);
+
+    p.ClearUserProperties();
+    ASSERT_FALSE(p.IsAdvancedFormatNeeded());
+  }
+
+  {
+    WebServiceParameters p(v);
+    ASSERT_TRUE(p.IsAdvancedFormatNeeded());
+    ASSERT_TRUE(p.GetHttpHeaders().empty());
+
+    std::set<std::string> tmp;
+    p.ListUserProperties(tmp);
+    ASSERT_EQ(2u, tmp.size());
+    ASSERT_NE(tmp.find("a"), tmp.end());
+    ASSERT_NE(tmp.find("Hello"), tmp.end());
+    ASSERT_EQ(tmp.find("hello"), tmp.end());
+
+    std::string s;
+    ASSERT_TRUE(p.LookupUserProperty(s, "a"));      ASSERT_TRUE(s == "b");
+    ASSERT_TRUE(p.LookupUserProperty(s, "Hello"));  ASSERT_TRUE(s == "world");
+    ASSERT_FALSE(p.LookupUserProperty(s, "hello"));
+  }
+}