-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add algorithm to visit Server/Defs/Nodes on a path
This algorithm allows visiting the nodes on a given path and to collect the applicable per node permission used for Authorisation Re ECFLOW-1960
- Loading branch information
1 parent
ff399ea
commit 279d84d
Showing
6 changed files
with
307 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/* | ||
* Copyright 2009- ECMWF. | ||
* | ||
* This software is licensed under the terms of the Apache Licence version 2.0 | ||
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. | ||
* In applying this licence, ECMWF does not waive the privileges and immunities | ||
* granted to it by virtue of its status as an intergovernmental organisation | ||
* nor does it submit to any jurisdiction. | ||
*/ | ||
|
||
#ifndef ecflow_base_Algorithms_hpp | ||
#define ecflow_base_Algorithms_hpp | ||
|
||
#include <string> | ||
|
||
#include <boost/algorithm/string.hpp> | ||
#include <boost/tokenizer.hpp> | ||
|
||
#include "ecflow/base/AbstractServer.hpp" | ||
#include "ecflow/core/Result.hpp" | ||
#include "ecflow/node/Defs.hpp" | ||
|
||
namespace ecf { | ||
|
||
/// | ||
/// Represents a path in the server's hierarchy | ||
/// | ||
/// A path object is always represents a valid path (even if it does not exist). | ||
/// | ||
/// A path with 0 tokens represents the root of the hierarchy (represented by a slash, "/"). | ||
/// A path with N tokens represents a path from the root to a node. | ||
/// | ||
/// Multiple consecutive separators (i.e. slashes, "/") are treated as a single slash. | ||
/// | ||
struct Path | ||
{ | ||
static Result<Path> make(const std::string& path) { | ||
if (path.empty()) { | ||
return Result<Path>::failure("Invalid path: '" + path + "' (cannot be empty)"); | ||
} | ||
|
||
if (path == "/") { | ||
return Result<Path>::success(Path(std::vector<std::string>())); | ||
} | ||
|
||
std::vector<std::string> tokens; | ||
boost::tokenizer<boost::char_separator<char>> tokenizer(path, boost::char_separator<char>("/ ")); | ||
for (const auto& token : tokenizer) { | ||
tokens.push_back(token); | ||
} | ||
return Result<Path>::success(Path(std::move(tokens))); | ||
} | ||
|
||
[[nodiscard]] std::string to_string() const { | ||
if (tokens_.empty()) { | ||
return "/"; | ||
} | ||
|
||
std::string result; | ||
for (auto&& token : tokens_) { | ||
result += '/'; | ||
result += token; | ||
} | ||
return result; | ||
} | ||
|
||
[[nodiscard]] bool empty() const { return tokens_.empty(); } | ||
[[nodiscard]] size_t size() const { return tokens_.size(); } | ||
[[nodiscard]] const std::string& operator[](size_t idx) const { return tokens_[idx]; } | ||
|
||
auto begin() const { return tokens_.begin(); } | ||
auto end() const { return tokens_.end(); } | ||
|
||
private: | ||
explicit Path(std::vector<std::string> tokens) : tokens_(std::move(tokens)) {} | ||
|
||
std::vector<std::string> tokens_; | ||
}; | ||
|
||
template <typename PREDICATE> | ||
void visit(const Defs& defs, const Path& path, PREDICATE& predicate) { | ||
|
||
// a. Visit the server 'definitions' | ||
predicate(defs); | ||
|
||
// b. Visit each one of the 'nodes' along the given path | ||
|
||
node_ptr current = nullptr; | ||
for (auto&& token : path) { | ||
if (current == nullptr) { | ||
current = defs.findSuite(token); | ||
} | ||
else { | ||
current = current->find_immediate_child(token); | ||
} | ||
predicate(*current); | ||
} | ||
|
||
} | ||
|
||
template <typename PREDICATE> | ||
void visit(const AbstractServer& server, const Path& path, PREDICATE& predicate) { | ||
|
||
// Visit the 'server' | ||
predicate(server); | ||
|
||
if (path.empty()) { | ||
return; | ||
} | ||
|
||
visit(*server.defs(), path, predicate); | ||
} | ||
|
||
|
||
} // namespace ecf | ||
|
||
#endif // ecflow_base_Algorithms_hpp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
/* | ||
* Copyright 2009- ECMWF. | ||
* | ||
* This software is licensed under the terms of the Apache Licence version 2.0 | ||
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. | ||
* In applying this licence, ECMWF does not waive the privileges and immunities | ||
* granted to it by virtue of its status as an intergovernmental organisation | ||
* nor does it submit to any jurisdiction. | ||
*/ | ||
|
||
#define BOOST_TEST_MODULE Test_Base | ||
#include <boost/test/included/unit_test.hpp> | ||
|
||
#include "MockServer.hpp" | ||
#include "ecflow/base/Algorithms.hpp" | ||
#include "ecflow/node/Family.hpp" | ||
#include "ecflow/server/BaseServer.hpp" | ||
#include "ecflow/test/scaffold/Naming.hpp" | ||
|
||
BOOST_AUTO_TEST_SUITE(U_Base) | ||
|
||
BOOST_AUTO_TEST_SUITE(T_Path) | ||
|
||
BOOST_AUTO_TEST_CASE(test_cannot_create_path_from_empty_string) { | ||
ECF_NAME_THIS_TEST(); | ||
|
||
auto result = ecf::Path::make(""); | ||
BOOST_CHECK_MESSAGE(!result.ok(), "expected !ok"); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(test_can_create_path_from_root_only) { | ||
ECF_NAME_THIS_TEST(); | ||
|
||
auto result = ecf::Path::make("/"); | ||
BOOST_CHECK(result.ok()); | ||
auto path = result.value(); | ||
BOOST_CHECK(path.empty()); | ||
BOOST_CHECK_EQUAL(path.size(), 0ul); | ||
BOOST_CHECK_EQUAL(path.to_string(), "/"); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(test_can_create_path_with_single_token) { | ||
ECF_NAME_THIS_TEST(); | ||
|
||
auto result = ecf::Path::make("/suite"); | ||
BOOST_CHECK(result.ok()); | ||
auto path = result.value(); | ||
BOOST_CHECK(!path.empty()); | ||
BOOST_CHECK_EQUAL(path.size(), 1ul); | ||
BOOST_CHECK_EQUAL(path[0], "suite"); | ||
BOOST_CHECK_EQUAL(path.to_string(), "/suite"); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(test_can_create_path_with_multiple_tokens) { | ||
ECF_NAME_THIS_TEST(); | ||
|
||
auto result = ecf::Path::make("/suite/family/task"); | ||
BOOST_CHECK(result.ok()); | ||
auto path = result.value(); | ||
BOOST_CHECK(!path.empty()); | ||
BOOST_CHECK_EQUAL(path.size(), 3ul); | ||
BOOST_CHECK_EQUAL(path[0], "suite"); | ||
BOOST_CHECK_EQUAL(path[1], "family"); | ||
BOOST_CHECK_EQUAL(path[2], "task"); | ||
BOOST_CHECK_EQUAL(path.to_string(), "/suite/family/task"); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(test_can_create_path_with_empty_tokens) { | ||
ECF_NAME_THIS_TEST(); | ||
|
||
auto result = ecf::Path::make("///suite///family///task"); | ||
BOOST_CHECK(result.ok()); | ||
auto path = result.value(); | ||
BOOST_CHECK(!path.empty()); | ||
BOOST_CHECK_EQUAL(path.size(), 3ul); | ||
BOOST_CHECK_EQUAL(path[0], "suite"); | ||
BOOST_CHECK_EQUAL(path[1], "family"); | ||
BOOST_CHECK_EQUAL(path[2], "task"); | ||
BOOST_CHECK_EQUAL(path.to_string(), "/suite/family/task"); | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() // T_Path | ||
|
||
BOOST_AUTO_TEST_SUITE(T_Algorithms) | ||
|
||
BOOST_AUTO_TEST_CASE(test_can_visit_defs) { | ||
ECF_NAME_THIS_TEST(); | ||
|
||
Defs defs; | ||
suite_ptr s = defs.add_suite("suite"); | ||
family_ptr f = s->add_family("family"); | ||
task_ptr t = f->add_task("task"); | ||
|
||
struct Visitor | ||
{ | ||
void operator()(const Defs& defs) { collected.push_back("defs"); } | ||
void operator()(const Node& s) { collected.push_back("node: " + s.name()); } | ||
|
||
std::vector<std::string> collected; | ||
}; | ||
|
||
auto path = ecf::Path::make("/suite/family/task").value(); | ||
Visitor visitor; | ||
ecf::visit(defs, path, visitor); | ||
|
||
BOOST_CHECK_EQUAL(visitor.collected.size(), 4ul); | ||
BOOST_CHECK_EQUAL(visitor.collected[0], "defs"); | ||
BOOST_CHECK_EQUAL(visitor.collected[1], "node: suite"); | ||
BOOST_CHECK_EQUAL(visitor.collected[2], "node: family"); | ||
BOOST_CHECK_EQUAL(visitor.collected[3], "node: task"); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(test_can_visit_server) { | ||
ECF_NAME_THIS_TEST(); | ||
|
||
Defs defs; | ||
suite_ptr s = defs.add_suite("suite"); | ||
family_ptr f = s->add_family("family"); | ||
task_ptr t = f->add_task("task"); | ||
|
||
MockServer server(&defs); | ||
|
||
struct Visitor | ||
{ | ||
void operator()(const AbstractServer& server) { collected.push_back("server"); } | ||
void operator()(const Defs& defs) { collected.push_back("defs"); } | ||
void operator()(const Node& s) { collected.push_back("node: " + s.name()); } | ||
|
||
std::vector<std::string> collected; | ||
}; | ||
|
||
auto path = ecf::Path::make("/suite/family/task").value(); | ||
Visitor visitor; | ||
ecf::visit(server, path, visitor); | ||
|
||
BOOST_CHECK_EQUAL(visitor.collected.size(), 5ul); | ||
BOOST_CHECK_EQUAL(visitor.collected[0], "server"); | ||
BOOST_CHECK_EQUAL(visitor.collected[1], "defs"); | ||
BOOST_CHECK_EQUAL(visitor.collected[2], "node: suite"); | ||
BOOST_CHECK_EQUAL(visitor.collected[3], "node: family"); | ||
BOOST_CHECK_EQUAL(visitor.collected[4], "node: task"); | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() // T_Algorithms | ||
|
||
BOOST_AUTO_TEST_SUITE_END() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.