config: creating templates for a specific context
Templates for new peers must preserve the existing values of shared properties, otherwise writing back the template might unintentionally overwrite those. Because this depends on getting the right context, some utility functions from SyncConfig were made public. Instead of "" as normal form for the default context, "@default" is now used. This is more readable in error messages and while debugging. This patch does this only for the syncevo-dbus-server. SyncConfig::createServerTemplate() knows nothing about contexts at the moment. The command line has not been updated yet (MB #8048). When doing that it might turn out that changing the semantic of SyncConfig::createServerTemplate() and moving some syncevo-dbus-server.cpp code into SyncConfig.cpp is the better solution.
This commit is contained in:
parent
50e8a7ca9d
commit
6207e80bec
|
@ -189,6 +189,10 @@ public:
|
|||
|
||||
/** Session.GetDatabases() */
|
||||
void getDatabases(const string &sourceName, SourceDatabases_t &databases);
|
||||
|
||||
private:
|
||||
/** utility method which constructs a SyncConfig which references a local configuration (never a template) */
|
||||
boost::shared_ptr<SyncConfig> getLocalConfig(const std::string &configName);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1146,6 +1150,28 @@ void ReadOperations::getConfigs(bool getTemplates, std::vector<std::string> &con
|
|||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<SyncConfig> ReadOperations::getLocalConfig(const string &configName)
|
||||
{
|
||||
string peer, context;
|
||||
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(configName),
|
||||
peer, context);
|
||||
|
||||
boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(configName));
|
||||
// the default configuration can always be opened for reading,
|
||||
// everything else must exist
|
||||
if (context != "default" &&
|
||||
!syncConfig->exists()) {
|
||||
SE_THROW_EXCEPTION(NoSuchConfig, "No configuration '" + configName + "' found");
|
||||
}
|
||||
|
||||
// TODO: handle temporary configs (MB #8116)
|
||||
// - if config was set temporarily, it doesn't have to exist on disk =>
|
||||
// the check above is too strict
|
||||
// - set temporary properties as filters
|
||||
|
||||
return syncConfig;
|
||||
}
|
||||
|
||||
void ReadOperations::getConfig(bool getTemplate,
|
||||
Config_t &config)
|
||||
{
|
||||
|
@ -1153,18 +1179,30 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
boost::shared_ptr<SyncConfig> syncConfig;
|
||||
/** get server template */
|
||||
if(getTemplate) {
|
||||
syncConfig = SyncConfig::createServerTemplate(m_configName);
|
||||
string peer, context;
|
||||
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
|
||||
peer, context);
|
||||
|
||||
syncConfig = SyncConfig::createServerTemplate(peer);
|
||||
if(!syncConfig.get()) {
|
||||
SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + m_configName + "' found");
|
||||
}
|
||||
} else { ///< get a matching server configuration
|
||||
boost::shared_ptr<SyncConfig> from;
|
||||
syncConfig.reset(new SyncConfig(m_configName));
|
||||
// the default configuration can always be opened for reading,
|
||||
// everything else must exist
|
||||
if (!m_configName.empty() && !syncConfig->exists()) {
|
||||
SE_THROW_EXCEPTION(NoSuchConfig, "No configuration '" + m_configName + "' found");
|
||||
|
||||
// use the shared properties from the right context as filter
|
||||
// so that the returned template preserves existing properties
|
||||
boost::shared_ptr<SyncConfig> shared = getLocalConfig(string("@") + context);
|
||||
|
||||
ConfigProps props;
|
||||
shared->getProperties()->readProperties(props);
|
||||
syncConfig->setConfigFilter(true, "", props);
|
||||
BOOST_FOREACH(std::string source, shared->getSyncSources()) {
|
||||
SyncSourceNodes nodes = shared->getSyncSourceNodes(source, "");
|
||||
props.clear();
|
||||
nodes.getProperties()->readProperties(props);
|
||||
syncConfig->setConfigFilter(false, source, props);
|
||||
}
|
||||
} else {
|
||||
syncConfig = getLocalConfig(m_configName);
|
||||
}
|
||||
|
||||
/** get sync properties and their values */
|
||||
|
|
|
@ -67,7 +67,7 @@ void ConfigProperty::throwValueError(const ConfigNode &node, const string &name,
|
|||
SyncContext::throwError(node.getName() + ": " + name + " = " + value + ": " + error);
|
||||
}
|
||||
|
||||
string SyncConfig::normalizePeerString(const string &peer)
|
||||
string SyncConfig::normalizeConfigString(const string &peer)
|
||||
{
|
||||
string normal = peer;
|
||||
boost::to_lower(normal);
|
||||
|
@ -76,9 +76,24 @@ string SyncConfig::normalizePeerString(const string &peer)
|
|||
} else if (boost::ends_with(normal, "@")) {
|
||||
normal.resize(normal.size() - 1);
|
||||
}
|
||||
if (normal.empty()) {
|
||||
normal = "@default";
|
||||
}
|
||||
return normal;
|
||||
}
|
||||
|
||||
void SyncConfig::splitConfigString(const string &config, string &peer, string &context)
|
||||
{
|
||||
string::size_type at = config.rfind('@');
|
||||
if (at != config.npos) {
|
||||
peer = config.substr(0, at);
|
||||
context = config.substr(at + 1);
|
||||
} else {
|
||||
peer = config;
|
||||
context = "default";
|
||||
}
|
||||
}
|
||||
|
||||
SyncConfig::SyncConfig() :
|
||||
m_layout(HTTP_SERVER_LAYOUT) // use more compact layout with shorter paths and less source nodes
|
||||
{
|
||||
|
@ -113,7 +128,7 @@ SyncConfig::SyncConfig(const string &peer,
|
|||
|
||||
string root;
|
||||
|
||||
m_peer = normalizePeerString(peer);
|
||||
m_peer = normalizeConfigString(peer);
|
||||
|
||||
// except for SHARED_LAYOUT (set below),
|
||||
// everything is below the directory called like
|
||||
|
@ -139,13 +154,7 @@ SyncConfig::SyncConfig(const string &peer,
|
|||
} else {
|
||||
// check whether config name specifies a context,
|
||||
// otherwise use "default"
|
||||
string::size_type at = m_peerPath.rfind('@');
|
||||
if (at != m_peerPath.npos) {
|
||||
m_contextPath = m_peerPath.substr(at + 1);
|
||||
m_peerPath.resize(at);
|
||||
} else {
|
||||
m_contextPath = "default";
|
||||
}
|
||||
splitConfigString(m_peer, m_peerPath, m_contextPath);
|
||||
if (!m_peerPath.empty()) {
|
||||
m_peerPath = m_contextPath + "/peers/" + m_peerPath;
|
||||
}
|
||||
|
@ -264,7 +273,7 @@ void SyncConfig::addPeers(const string &root,
|
|||
if (!access((root + "/" + peerPath).c_str(), F_OK)) {
|
||||
// not a real HTTP server, search for peers
|
||||
BOOST_FOREACH(const string &peer, tree.getChildren(peerPath)) {
|
||||
res.push_back(pair<string, string>(normalizePeerString(peer + "@" + server),
|
||||
res.push_back(pair<string, string>(normalizeConfigString(peer + "@" + server),
|
||||
root + "/" + peerPath + "/" + peer));
|
||||
}
|
||||
} else if (!access((root + "/" + server + "/" + configname).c_str(), F_OK)) {
|
||||
|
|
|
@ -890,6 +890,21 @@ class SyncConfig {
|
|||
*/
|
||||
static ConfigPropertyRegistry &getRegistry();
|
||||
|
||||
/**
|
||||
* Normalize a config string:
|
||||
* - lower case
|
||||
* - @default stripped
|
||||
* - empty string replaced with "@default"
|
||||
*/
|
||||
static string normalizeConfigString(const string &peer);
|
||||
|
||||
/**
|
||||
* Split a config string (normalized or not) into the peer part
|
||||
* (before final @) and the context (after that @, not including
|
||||
* it), return "default" as context if not specified otherwise.
|
||||
*/
|
||||
static void splitConfigString(const string &config, string &peer, string &context);
|
||||
|
||||
/**
|
||||
* Replaces the property filter of either the sync properties or
|
||||
* all sources. This can be used to e.g. temporarily override
|
||||
|
@ -1184,13 +1199,6 @@ private:
|
|||
SyncEvolution >= 1.0 */
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize a peer string:
|
||||
* - lower case
|
||||
* - @default stripped
|
||||
*/
|
||||
static string normalizePeerString(const string &peer);
|
||||
|
||||
/**
|
||||
* scans for peer configurations
|
||||
* @param root absolute directory path
|
||||
|
|
|
@ -1463,5 +1463,27 @@ class TestMultipleConfigs(unittest.TestCase, DBusUtil):
|
|||
self.failUnlessEqual(peers, ['foo@other_context'])
|
||||
self.session.Detach()
|
||||
|
||||
def testTemplates(self):
|
||||
'''templates reuse common properties'''
|
||||
self.setupConfigs()
|
||||
|
||||
# deviceID must be shared and thus be reused in templates
|
||||
self.setUpSession("")
|
||||
config = self.session.GetConfig(False, utf8_strings=True)
|
||||
config[""]["DEVICEID"] = "shared-device-identifier"
|
||||
self.session.SetConfig(True, False, config, utf8_strings=True)
|
||||
config = self.server.GetConfig("", False, utf8_strings=True)
|
||||
self.failUnlessEqual(config[""]["deviceId"], "shared-device-identifier")
|
||||
|
||||
# get template for default context
|
||||
config = self.server.GetConfig("scheduleworld", True, utf8_strings=True)
|
||||
self.failUnlessEqual(config[""]["defaultPeer"], "foobar_peer")
|
||||
self.failUnlessEqual(config[""]["deviceId"], "shared-device-identifier")
|
||||
|
||||
# now for @other_context - different device ID!
|
||||
config = self.server.GetConfig("scheduleworld@other_context", True, utf8_strings=True)
|
||||
self.failUnlessEqual(config[""]["defaultPeer"], "foobar_peer")
|
||||
self.failIfEqual(config[""]["deviceId"], "shared-device-identifier")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in New Issue