syncevo-dbus-server: removing/clearing of properties in shared configs (MB# 8059)
This patch introduces additional semantic for SetConfig(update=False, {}) (= removing configuration) and SetConfig(update=False, {<some sources removed>} (= removing sources): when the config view is for a specific peer, then only properties specific to that peer are removed. When the view is peer-independent, it removes the complete configuration (including all peers) resp. removes the shared and per-peer source settings in all peers. Global properties are never removed. With this patch, they can be read by selecting the "@default" (= "") config view. Previously, reading this view was rejected unless it contained real config nodes. ConfigTree::removeSubtree() was merged into ConfigTree::remove(). The functions were clearly related. The tree remains usable in the revised remove() call. SyncConfig::remove() did not work since the introduction of the "nodes not in use" check in FileConfigTree::reset(). SyncConfig has to drop all references to config nodes before FileConfigTree::remove() can do its job. This is done via a new SyncConfig::makeVolatile(). test-dbus.py was updated to cover the new remove/clear semantic. Because clearing sources with the "dummy-test-for-config" view only removes some source properties, the remaining ones are still reported in testClearConfigSources, breaking it. The test is superseeded by the new tests and thus was removed.
This commit is contained in:
parent
cc107655f6
commit
7bd113e946
|
@ -25,8 +25,19 @@
|
|||
directory if it is empty) which are not referenced by a
|
||||
key in the configuration are removed. Setting a completely
|
||||
empty configuration with "update=FALSE" can thus be used
|
||||
to remove the entire
|
||||
configuration.
|
||||
to remove the entire configuration.
|
||||
|
||||
When a specific peer was selected via the configuration
|
||||
name, clearing and removing properties is done only
|
||||
for the peer-specific properties.
|
||||
|
||||
When no specific peer was selected, setting an empty
|
||||
configuration with "update=FALSE" removes all source
|
||||
settings and all peers. This allows starting from scratch;
|
||||
setting a non-empty configuration with "update=FALSE"
|
||||
will replace the peer-independent source properties with
|
||||
those that are sent in the new configuration and remove
|
||||
the sources which are not listed, also in all peers.
|
||||
</doc:summary></doc:doc>
|
||||
</arg>
|
||||
<arg type="b" name="temporary" direction="in">
|
||||
|
|
|
@ -1160,7 +1160,9 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
} else { ///< get a matching server configuration
|
||||
boost::shared_ptr<SyncConfig> from;
|
||||
syncConfig.reset(new SyncConfig(m_configName));
|
||||
if (!syncConfig->exists()) {
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,9 +70,14 @@ class ConfigTree {
|
|||
/** ensure that all changes are saved persistently */
|
||||
virtual void flush() = 0;
|
||||
|
||||
/** remove all configuration nodes and (if based on files)
|
||||
directories created for them, if empty after file removal */
|
||||
virtual void remove() = 0;
|
||||
/**
|
||||
* Remove all configuration nodes below and including a certain
|
||||
* path and (if based on files) directories created for them, if
|
||||
* empty after file removal.
|
||||
*
|
||||
* The nodes must not be in use for this to work.
|
||||
*/
|
||||
virtual void remove(const string &path) = 0;
|
||||
|
||||
/** a string identifying the root of the configuration - exact meaning varies */
|
||||
virtual string getRootPath() const = 0;
|
||||
|
@ -108,12 +113,6 @@ class ConfigTree {
|
|||
* returns names of all existing nodes beneath the given path
|
||||
*/
|
||||
virtual list<string> getChildren(const string &path) = 0;
|
||||
|
||||
/**
|
||||
* remove config nodes beneath the given path. If no other
|
||||
* files, the directory will be removed too.
|
||||
*/
|
||||
virtual void removeSubtree(const string &path) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -84,10 +84,11 @@ static bool rm_filter(const string &path, bool isDir)
|
|||
}
|
||||
}
|
||||
|
||||
void FileConfigTree::remove()
|
||||
void FileConfigTree::remove(const string &path)
|
||||
{
|
||||
reset();
|
||||
rm_r(m_root, rm_filter);
|
||||
string fullpath = m_root + "/" + path;
|
||||
clearNodes(fullpath);
|
||||
rm_r(fullpath, rm_filter);
|
||||
}
|
||||
|
||||
void FileConfigTree::reset()
|
||||
|
@ -106,13 +107,6 @@ void FileConfigTree::reset()
|
|||
m_nodes.clear();
|
||||
}
|
||||
|
||||
void FileConfigTree::removeSubtree(const string &name)
|
||||
{
|
||||
string fullpath = m_root + "/" + name;
|
||||
clearNodes(fullpath);
|
||||
rm_r(fullpath, rm_filter);
|
||||
}
|
||||
|
||||
void FileConfigTree::clearNodes(const string &fullpath)
|
||||
{
|
||||
NodeCache_t::iterator it;
|
||||
|
|
|
@ -53,13 +53,12 @@ class FileConfigTree : public ConfigTree {
|
|||
/* ConfigTree API */
|
||||
virtual string getRootPath() const;
|
||||
virtual void flush();
|
||||
virtual void remove();
|
||||
virtual void remove(const string &path);
|
||||
virtual void reset();
|
||||
virtual boost::shared_ptr<ConfigNode> open(const string &path,
|
||||
PropertyType type,
|
||||
const string &otherId = string(""));
|
||||
list<string> getChildren(const string &path);
|
||||
virtual void removeSubtree(const string &path);
|
||||
|
||||
private:
|
||||
/**
|
||||
|
|
|
@ -88,6 +88,11 @@ SyncConfig::SyncConfig() :
|
|||
|
||||
m_peerPath =
|
||||
m_contextPath = "volatile";
|
||||
makeVolatile();
|
||||
}
|
||||
|
||||
void SyncConfig::makeVolatile()
|
||||
{
|
||||
m_tree.reset(new VolatileConfigTree());
|
||||
m_peerNode.reset(new VolatileConfigNode());
|
||||
m_hiddenPeerNode = m_peerNode;
|
||||
|
@ -565,8 +570,14 @@ void SyncConfig::flush()
|
|||
|
||||
void SyncConfig::remove()
|
||||
{
|
||||
m_tree->remove();
|
||||
m_tree.reset(new VolatileConfigTree());
|
||||
boost::shared_ptr<ConfigTree> tree = m_tree;
|
||||
|
||||
// stop using the config nodes, they might get removed now
|
||||
makeVolatile();
|
||||
|
||||
tree->remove(m_peerPath.empty() ?
|
||||
m_contextPath :
|
||||
m_peerPath);
|
||||
}
|
||||
|
||||
boost::shared_ptr<PersistentSyncSourceConfig> SyncConfig::getSyncSourceConfig(const string &name)
|
||||
|
@ -1227,20 +1238,25 @@ void SyncConfig::removeSyncSource(const string &name)
|
|||
string pathName;
|
||||
|
||||
if (m_layout == SHARED_LAYOUT) {
|
||||
// removed share source properties...
|
||||
pathName = m_contextPath + "/sources/" + lower;
|
||||
m_tree->removeSubtree(pathName);
|
||||
// ... and the peer-specific ones of *all* peers
|
||||
BOOST_FOREACH(const std::string peer,
|
||||
m_tree->getChildren("peers")) {
|
||||
m_tree->removeSubtree(string("peers/") + peer + "/sources/" + lower);
|
||||
if (m_peerPath.empty()) {
|
||||
// removed shared source properties...
|
||||
pathName = m_contextPath + "/sources/" + lower;
|
||||
m_tree->remove(pathName);
|
||||
// ... and the peer-specific ones of *all* peers
|
||||
BOOST_FOREACH(const std::string peer,
|
||||
m_tree->getChildren("peers")) {
|
||||
m_tree->remove(string("peers/") + peer + "/sources/" + lower);
|
||||
}
|
||||
} else {
|
||||
// remove only inside the selected peer
|
||||
m_tree->remove(m_peerPath + "/sources/" + lower);
|
||||
}
|
||||
} else {
|
||||
// remove the peer-specific ones
|
||||
pathName = m_peerPath +
|
||||
(m_layout == SYNC4J_LAYOUT ? "spds/sources/" : "sources/") +
|
||||
lower;
|
||||
m_tree->removeSubtree(pathName);
|
||||
m_tree->remove(pathName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -872,14 +872,16 @@ class SyncConfig {
|
|||
void flush();
|
||||
|
||||
/**
|
||||
* Remove the configuration. The config object itself is still
|
||||
* valid afterwards, but empty and cannot be flushed.
|
||||
* Remove the configuration. Config directories are removed if
|
||||
* empty.
|
||||
*
|
||||
* When the configuration is peer-specific, only the peer's
|
||||
* properties and config nodes are removed. Otherwise the complete
|
||||
* configuration is removed, including all peers.
|
||||
*
|
||||
* Does *not* remove logs associated with the configuration.
|
||||
* For that use the logdir handling in SyncContext
|
||||
* before removing the configuration.
|
||||
*
|
||||
* The config directory is removed if it is empty.
|
||||
*/
|
||||
void remove();
|
||||
|
||||
|
@ -982,18 +984,26 @@ class SyncConfig {
|
|||
void setSourceDefaults(const string &name, bool force = true);
|
||||
|
||||
/**
|
||||
* remove sync source configuration. And remove the directory
|
||||
* if it has no other files
|
||||
* Remove sync source configuration. And remove the directory
|
||||
* if it has no other files.
|
||||
*
|
||||
* When the configuration is peer-specific, only the peer's
|
||||
* properties are removed. Otherwise the complete source
|
||||
* configuration is removed, including properties stored
|
||||
* for in any of the peers.
|
||||
*/
|
||||
void removeSyncSource(const string &name);
|
||||
|
||||
/**
|
||||
* clear existing visible properties in config.ini
|
||||
* clear existing visible source properties selected by the
|
||||
* configuration: with or without peer-specific properties,
|
||||
* depending on the current view
|
||||
*/
|
||||
void clearSyncSourceProperties(const string &name);
|
||||
|
||||
/**
|
||||
* clear all global sync properties
|
||||
* clear all global sync properties, with or without
|
||||
* peer-specific properties, depending on the current view
|
||||
*/
|
||||
void clearSyncProperties();
|
||||
|
||||
|
@ -1205,6 +1215,11 @@ private:
|
|||
const std::string &configname,
|
||||
SyncConfig::ServerList &res);
|
||||
|
||||
/**
|
||||
* set tree and nodes to VolatileConfigTree/Node
|
||||
*/
|
||||
void makeVolatile();
|
||||
|
||||
/**
|
||||
* String that identifies the peer, see constructor.
|
||||
* This is a normalized string (normalizePeerString()).
|
||||
|
|
|
@ -786,21 +786,7 @@ class TestSessionAPIsDummy(unittest.TestCase, DBusUtil):
|
|||
config = self.session.GetConfig(False, utf8_strings=True)
|
||||
except dbus.DBusException, ex:
|
||||
self.failUnlessEqual(str(ex),
|
||||
"org.syncevolution.NoSuchConfig: No configuration 'dummy-test' found")
|
||||
|
||||
def testClearConfigSources(self):
|
||||
""" test sources related configs are cleared correctly. """
|
||||
self.setupConfig()
|
||||
config1 = {
|
||||
"" : { "syncURL" : "http://my.funambol.com/sync",
|
||||
"username" : "unknown",
|
||||
"password" : "secret",
|
||||
"deviceId" : "foo"
|
||||
}
|
||||
}
|
||||
self.session.SetConfig(False, False, config1, utf8_strings=True)
|
||||
config2 = self.session.GetConfig(False, utf8_strings=True)
|
||||
self.failUnlessEqual(config2, config1)
|
||||
"org.syncevolution.NoSuchConfig: No configuration 'dummy-test' found")
|
||||
|
||||
def testCheckSourceNoConfig(self):
|
||||
""" test the right error is reported when the server doesn't exist """
|
||||
|
@ -1287,7 +1273,9 @@ class TestMultipleConfigs(unittest.TestCase, DBusUtil):
|
|||
self.session.SetConfig(True, False,
|
||||
{ "" : { "defaultPeer" : "foobar_peer",
|
||||
"syncURL": "http://scheduleworld" },
|
||||
"source/calendar" : { "uri" : "cal3" },
|
||||
"source/addressbook" : { "evolutionsource": "Personal",
|
||||
"sync" : "two-way",
|
||||
"uri": "card3" } },
|
||||
utf8_strings=True)
|
||||
self.session.Detach()
|
||||
|
@ -1299,7 +1287,9 @@ class TestMultipleConfigs(unittest.TestCase, DBusUtil):
|
|||
self.failUnlessEqual(config["source/addressbook"]["evolutionsource"], "Personal")
|
||||
self.session.SetConfig(True, False,
|
||||
{ "" : { "syncURL": "http://funambol" },
|
||||
"source/calendar" : { "uri" : "cal" },
|
||||
"source/addressbook" : { "evolutionsource": "Work",
|
||||
"sync" : "refresh-from-client",
|
||||
"uri": "card" } },
|
||||
utf8_strings=True)
|
||||
self.session.Detach()
|
||||
|
@ -1360,6 +1350,36 @@ class TestMultipleConfigs(unittest.TestCase, DBusUtil):
|
|||
self.failUnlessEqual(config["source/addressbook"]["uri"], "card3")
|
||||
self.session.Detach()
|
||||
|
||||
# remove "addressbook" source in "foo"
|
||||
self.setUpSession("foo")
|
||||
config = self.session.GetConfig(False, utf8_strings=True)
|
||||
del config["source/addressbook"]
|
||||
self.session.SetConfig(False, False, config, utf8_strings=True)
|
||||
self.session.Detach()
|
||||
|
||||
# "addressbook" still exists in "foo" but only with default values
|
||||
config = self.server.GetConfig("foo", False, utf8_strings=True)
|
||||
self.failIf("uri" in config["source/addressbook"])
|
||||
self.failIf("sync" in config["source/addressbook"])
|
||||
|
||||
# "addressbook" unchanged in "bar"
|
||||
config = self.server.GetConfig("bar", False, utf8_strings=True)
|
||||
self.failUnlessEqual(config["source/addressbook"]["uri"], "card")
|
||||
self.failUnlessEqual(config["source/addressbook"]["sync"], "refresh-from-client")
|
||||
|
||||
# remove "addressbook" everywhere
|
||||
self.setUpSession("")
|
||||
config = self.session.GetConfig(False, utf8_strings=True)
|
||||
del config["source/addressbook"]
|
||||
self.session.SetConfig(False, False, config, utf8_strings=True)
|
||||
self.session.Detach()
|
||||
|
||||
# "addressbook" gone in "foo" and "bar"
|
||||
config = self.server.GetConfig("foo", False, utf8_strings=True)
|
||||
self.failIf("source/addressbook" in config)
|
||||
config = self.server.GetConfig("bar", False, utf8_strings=True)
|
||||
self.failIf("source/addressbook" in config)
|
||||
|
||||
# check listing of peers while removing "bar"
|
||||
self.setUpSession("bar")
|
||||
peers = self.session.GetConfigs(False, utf8_strings=True)
|
||||
|
@ -1367,28 +1387,32 @@ class TestMultipleConfigs(unittest.TestCase, DBusUtil):
|
|||
[ "bar", "foo", "foo@other_context" ])
|
||||
peers2 = self.server.GetConfigs(False, utf8_strings=True)
|
||||
self.failUnlessEqual(peers, peers2)
|
||||
|
||||
# remove "foo"
|
||||
# remove "bar"
|
||||
self.session.SetConfig(False, False, {}, utf8_strings=True)
|
||||
|
||||
# The other peers should not have been affected, but currently
|
||||
# they are (MB #8059). Decide about this and remove the return.
|
||||
return
|
||||
|
||||
peers = self.server.GetConfigs(False, utf8_strings=True)
|
||||
self.failUnlessEqual(peers,
|
||||
[ "foo", "foo@other_context" ])
|
||||
self.session.Detach()
|
||||
config = self.GetConfig("foo", False, utf8_strings=True)
|
||||
|
||||
# other configs should not have been affected
|
||||
config = self.server.GetConfig("foo", False, utf8_strings=True)
|
||||
self.failUnlessEqual(config[""]["defaultPeer"], "foobar_peer")
|
||||
self.failUnlessEqual(config[""]["syncURL"], "http://scheduleworld")
|
||||
self.failUnlessEqual(config["source/addressbook"]["evolutionsource"], "Work")
|
||||
self.failUnlessEqual(config["source/addressbook"]["uri"], "card3")
|
||||
config = self.GetConfig("foo@other_context", False, utf8_strings=True)
|
||||
self.failUnlessEqual(config["source/calendar"]["uri"], "cal3")
|
||||
config = self.server.GetConfig("foo@other_context", False, utf8_strings=True)
|
||||
self.failUnlessEqual(config[""]["defaultPeer"], "foobar_peer")
|
||||
self.failUnlessEqual(config[""]["syncURL"], "http://scheduleworld2")
|
||||
self.failUnlessEqual(config["source/addressbook"]["evolutionsource"], "Play")
|
||||
self.failUnlessEqual(config["source/addressbook"]["uri"], "card30")
|
||||
|
||||
# remove complete config
|
||||
self.setUpSession("")
|
||||
self.session.SetConfig(False, False, {}, utf8_strings=True)
|
||||
config = self.session.GetConfig(False, utf8_strings=True)
|
||||
self.failUnlessEqual(config[""]["defaultPeer"], "foobar_peer")
|
||||
peers = self.server.GetConfigs(False, utf8_strings=True)
|
||||
self.failUnlessEqual(peers, ['foo@other_context'])
|
||||
self.session.Detach()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in New Issue