add assignment or operator for multiple name suggestions
includes tests and config update

Change-Id: I4facf792d89c5ce34bc09f5495c45d0a3bab458a
diff --git a/src/configuration.cpp b/src/configuration.cpp
index 5bad3bb..1386d13 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -23,6 +23,7 @@
 #include <ndn-cxx/util/io.hpp>
 #include <boost/filesystem.hpp>
 #include <name-assignments/assignment-funcs.hpp>
+#include <name-assignments/assignment-or.hpp>
 
 namespace ndn {
 namespace ndncert {
@@ -152,7 +153,7 @@
   m_nameAssignmentFunc = nullptr;
   auto nameAssignmentItems = configJson.get_child_optional(CONFIG_NAME_ASSIGNMENT);
   if (nameAssignmentItems) {
-    std::vector<NameAssignmentFunc> funcs;
+    std::list<NameAssignmentFunc> funcs;
     for (const auto item : *nameAssignmentItems) {
         auto factory = NameAssignmentFuncFactory::createNameAssignmentFuncFactory(item.first);
         if (!factory) {
@@ -167,10 +168,10 @@
     if (funcs.size() < 1) {
         BOOST_THROW_EXCEPTION(std::runtime_error("Empty assignment body supplied"));
     } else if (funcs.size() == 1) {
-        m_nameAssignmentFunc = funcs[0];
+        m_nameAssignmentFunc = *funcs.begin();
     } else {
-        //TODO "or" all the name function together as all suggestions
-        m_nameAssignmentFunc = funcs[0];
+        AssignmentOr orFunction;
+        m_nameAssignmentFunc = orFunction.getFunction(funcs);
     }
   }
 }
diff --git a/src/name-assignments/assignment-or.cpp b/src/name-assignments/assignment-or.cpp
new file mode 100644
index 0000000..cd1f306
--- /dev/null
+++ b/src/name-assignments/assignment-or.cpp
@@ -0,0 +1,73 @@
+//
+// Created by Tyler on 10/6/20.
+//
+
+#include <iosfwd>
+#include "assignment-or.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+_LOG_INIT(ndncert.assignment.or);
+
+NDNCERT_REGISTER_FUNCFACTORY(AssignmentOr, "or");
+
+AssignmentOr::AssignmentOr()
+    : NameAssignmentFuncFactory("or")
+{
+}
+
+NameAssignmentFunc
+AssignmentOr::getFunction(std::list<NameAssignmentFunc> funcs){
+    if (funcs.size() == 1) return *funcs.begin();
+    return OrAssignmentFunc(funcs);
+}
+
+NameAssignmentFunc
+AssignmentOr::getFunction(const std::string &factoryParam) {
+    std::list<NameAssignmentFunc> paramList;
+    std::stringstream ss;
+    ss << factoryParam;
+    JsonSection section;
+    try {
+        boost::property_tree::read_json(ss, section);
+    }
+    catch (const std::exception& error) {
+        BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to parse configuration for name assignment function or, ") + error.what()));
+    }
+    if (section.begin() == section.end()) {
+        BOOST_THROW_EXCEPTION(std::runtime_error("No JSON configuration found for name assignment function"));
+    }
+    for (const auto& item: section) {
+        auto factory = NameAssignmentFuncFactory::createNameAssignmentFuncFactory(item.first);
+        if (!factory) {
+            BOOST_THROW_EXCEPTION(std::runtime_error("Invalid assignment factory type"));
+        }
+        try {
+            paramList.push_back(factory->getFunction(item.second.data()));
+        } catch (const std::exception& e) {
+            BOOST_THROW_EXCEPTION(std::runtime_error("Error on creating function"));
+        }
+    }
+
+    return getFunction(paramList);
+}
+
+AssignmentOr::OrAssignmentFunc::OrAssignmentFunc(std::list<NameAssignmentFunc> funcList)
+    : m_funcList(std::move(funcList))
+{}
+
+std::vector<PartialName>
+AssignmentOr::OrAssignmentFunc::operator() (const std::vector<std::tuple<std::string, std::string>> params)
+{
+  std::vector<PartialName> nameList;
+  for (const auto& func : m_funcList) {
+      auto result = func(params);
+      nameList.insert(nameList.end(), result.begin(), result.end());
+  }
+
+  return nameList;
+}
+
+}
+}
diff --git a/src/name-assignments/assignment-or.hpp b/src/name-assignments/assignment-or.hpp
new file mode 100644
index 0000000..68ffd66
--- /dev/null
+++ b/src/name-assignments/assignment-or.hpp
@@ -0,0 +1,40 @@
+//
+// Created by Tyler on 10/6/20.
+//
+
+#ifndef NDNCERT_ASSIGNMENT_OR_HPP
+#define NDNCERT_ASSIGNMENT_OR_HPP
+
+#include "assignment-funcs.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+/**
+ * assign names base on client probe parameter
+ */
+class AssignmentOr: public NameAssignmentFuncFactory{
+public:
+  AssignmentOr();
+
+  NameAssignmentFunc getFunction(std::list<NameAssignmentFunc> funcs);
+
+  NameAssignmentFunc getFunction(const std::string &factoryParam) override;
+
+  class OrAssignmentFunc {
+  public:
+    OrAssignmentFunc(std::list<NameAssignmentFunc> funcList);
+
+    std::vector<PartialName>
+    operator() (const std::vector<std::tuple<std::string, std::string>> params);
+  private:
+    std::list<NameAssignmentFunc> m_funcList;
+  };
+
+};
+}
+}
+
+
+
+#endif //NDNCERT_ASSIGNMENT_OR_HPP
diff --git a/tests/unit-tests/config-files/config-ca-5 b/tests/unit-tests/config-files/config-ca-5
index 0d1e6b3..f867534 100644
--- a/tests/unit-tests/config-files/config-ca-5
+++ b/tests/unit-tests/config-files/config-ca-5
@@ -24,6 +24,8 @@
   ],
   "name-assignment":
   {
-     "param": "/group/email"
+     "param": "/group/email",
+     "param": "/group/name",
+     "random": ""
   }
 }
\ No newline at end of file
diff --git a/tests/unit-tests/configuration.t.cpp b/tests/unit-tests/configuration.t.cpp
index 6ad1577..87de3f2 100644
--- a/tests/unit-tests/configuration.t.cpp
+++ b/tests/unit-tests/configuration.t.cpp
@@ -58,8 +58,10 @@
   std::vector<std::tuple<std::string, std::string>> params;
   params.emplace_back("email", "1@1.edu");
   params.emplace_back("group", "irl");
-  BOOST_CHECK_EQUAL(config.m_nameAssignmentFunc(params).size(), 1);
+  params.emplace_back("name", "ndncert");
+  BOOST_CHECK_EQUAL(config.m_nameAssignmentFunc(params).size(), 3);
   BOOST_CHECK_EQUAL(config.m_nameAssignmentFunc(params)[0], Name("/irl/1@1.edu"));
+  BOOST_CHECK_EQUAL(config.m_nameAssignmentFunc(params)[1], Name("/irl/ndncert"));
 }
 
 BOOST_AUTO_TEST_CASE(CAConfigFileWithErrors)
diff --git a/tests/unit-tests/name-assignment.t.cpp b/tests/unit-tests/name-assignment.t.cpp
index 1fc7f6a..1e7dd0e 100644
--- a/tests/unit-tests/name-assignment.t.cpp
+++ b/tests/unit-tests/name-assignment.t.cpp
@@ -21,6 +21,7 @@
 #include <name-assignments/assignment-random.hpp>
 #include <name-assignments/assignment-param.hpp>
 #include <name-assignments/assignment-hash.hpp>
+#include <name-assignments/assignment-or.hpp>
 #include "test-common.hpp"
 
 namespace ndn {
@@ -71,6 +72,40 @@
   BOOST_CHECK_EQUAL(func(requirements).begin()->size(), 1);
 }
 
+BOOST_AUTO_TEST_CASE(NameAssignmentOr)
+{
+  AssignmentParam paramAssignment;
+  AssignmentOr orAssignment;
+  std::list<NameAssignmentFunc> func2Subfuncs;
+  auto func1 = orAssignment.getFunction(func2Subfuncs);
+  BOOST_CHECK_EQUAL(func1(std::vector<std::tuple<std::string, std::string>>()).size(), 0);
+
+  auto funcUnit = paramAssignment.getFunction("/abc/xyz/");
+  func2Subfuncs.push_back(funcUnit);
+  func2Subfuncs.push_back(funcUnit);
+  auto func2 = orAssignment.getFunction(func2Subfuncs);
+  std::list<NameAssignmentFunc> func3Subfuncs;
+  func3Subfuncs.push_back(func2);
+  func3Subfuncs.push_back(funcUnit);
+  auto func3 = orAssignment.getFunction(func3Subfuncs);
+  std::vector<std::tuple<std::string, std::string>> requirements;
+  requirements.emplace_back("abc", "123");
+  BOOST_CHECK_EQUAL(func3(requirements).size(), 0);
+  requirements.emplace_back("xyz", "789");
+  BOOST_CHECK_EQUAL(func3(requirements).size(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(NameAssignmentOrOperatorString)
+{
+  AssignmentOr orAssignment;
+  auto func = orAssignment.getFunction(R"({"param": "/abc/xyz/", "param": "/abc/xyz"})");
+  std::vector<std::tuple<std::string, std::string>> requirements;
+  requirements.emplace_back("abc", "123");
+  BOOST_CHECK_EQUAL(func(requirements).size(), 0);
+  requirements.emplace_back("xyz", "789");
+  BOOST_CHECK_EQUAL(func(requirements).size(), 2);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 }  // namespace tests