D-Bus API: allow setting different configs in the same session
Forcing D-Bus API users to start a new session for each config that they want to modify adds considerable complexity to clients, in particular for local sync. Relaxing that requirements is easy because the current implementation already only runs one session at a time. It's also only a minor restriction for future improvements: sessions which request access to all configs cannot run in parallel with other sessions. Thus this patch adds Session.SetNamedConfig() and (for the sake of completeness) Session.GetNamedConfig(). Temporary config changes via SetNamedConfig() are harder to implement and not currently supported. They are part of the API though, just in case.
This commit is contained in:
parent
c4e69e2e58
commit
bb710674da
|
@ -96,6 +96,13 @@
|
|||
"evolutionuser", "evolutionpassword"; semantic is unchanged
|
||||
</doc:definition>
|
||||
</doc:item>
|
||||
|
||||
<doc:item><doc:term>NamedConfig</doc:term>
|
||||
<doc:definition>Session.Get/SetNamedConfig()
|
||||
are implemented
|
||||
</doc:definition>
|
||||
</doc:item>
|
||||
|
||||
</doc:list>
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
|
@ -485,10 +492,13 @@
|
|||
<doc:item><doc:term>no-sync</doc:term>
|
||||
<doc:definition>session will not be used for running a synchronization</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item><doc:term>all-configs</doc:term>
|
||||
<doc:definition>session will provide read/write access to all configurations, via Get/SetNamedConfig()</doc:definition>
|
||||
</doc:item>
|
||||
</doc:list>
|
||||
</doc:description></doc:doc>
|
||||
<arg type="s" name="config" direction="in">
|
||||
<doc:doc><doc:summary>name of configuration to be created or used in session</doc:summary></doc:doc>
|
||||
<doc:doc><doc:summary>name of configuration to be created or used in session; typically this will be empty when used in combination with 'all-configs' and Get/SetNamedConfig()</doc:summary></doc:doc>
|
||||
</arg>
|
||||
<arg type="as" name="flags" direction="in">
|
||||
<doc:doc><doc:summary>optional flags</doc:summary></doc:doc>
|
||||
|
|
|
@ -48,9 +48,9 @@
|
|||
</method>
|
||||
|
||||
<method name="GetConfig">
|
||||
<doc:doc><doc:description>Get the configuration of the server</doc:description></doc:doc>
|
||||
<doc:doc><doc:description>Get the configuration identified by the name given to StartSession()</doc:description></doc:doc>
|
||||
<arg type="b" name="tmplate" direction="in">
|
||||
<doc:doc><doc:summary>if TRUE, will return a matching template configuration, otherwise will return a matching server configuration</doc:summary></doc:doc>
|
||||
<doc:doc><doc:summary>if TRUE, will return a matching template configuration, otherwise will return the stored configuration</doc:summary></doc:doc>
|
||||
</arg>
|
||||
<arg type="a{sa{ss}}" name="configuration" direction="out">
|
||||
<doc:doc><doc:summary>server configuration</doc:summary></doc:doc>
|
||||
|
@ -107,6 +107,32 @@
|
|||
<annotation name="com.trolltech.QtDBus.QtTypeName.In2" value="QStringMultiMap"/>
|
||||
</method>
|
||||
|
||||
<method name="GetNamedConfig">
|
||||
<doc:doc><doc:description>Get the configuration identified by
|
||||
the name given in the first argument; same as
|
||||
Server.GetConfig(), provided again in Session for the sake of
|
||||
completeness</doc:description></doc:doc>
|
||||
<arg type="s" name="name" direction="in">
|
||||
<doc:doc><doc:summary>configuration name</doc:summary></doc:doc>
|
||||
</arg>
|
||||
<arg type="b" name="tmplate" direction="in"/>
|
||||
<arg type="a{sa{ss}}" name="configuration" direction="out"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QStringMultiMap"/>
|
||||
</method>
|
||||
|
||||
<method name="SetNamedConfig">
|
||||
<doc:doc><doc:description>Same as SetConfig() except that the modified configuration is named explicitly</doc:description></doc:doc>
|
||||
<arg type="s" name="name" direction="in">
|
||||
<doc:doc><doc:summary>configuration name; if exactly the same as in StartSession() or StartSessionWithFlags(), then SetNamedConfig() behaves exactly like SetConfig() and none of the constraints for SetNamedConfig() apply</doc:summary></doc:doc>
|
||||
</arg>
|
||||
<arg type="b" name="update" direction="in"/>
|
||||
<arg type="b" name="temporary" direction="in">
|
||||
<doc:doc><doc:summary>temporary changes of the configuration are currently only supported for the configuration chosen when creating the session</doc:summary></doc:doc>
|
||||
</arg>
|
||||
<arg type="a{sa{ss}}" name="configuration" direction="in"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.In2" value="QStringMultiMap"/>
|
||||
</method>
|
||||
|
||||
<method name="GetReports">
|
||||
<doc:doc><doc:description>Get synchronization reports for the server</doc:description></doc:doc>
|
||||
<arg type="u" name="start" direction="in">
|
||||
|
|
|
@ -96,6 +96,13 @@ boost::shared_ptr<DBusUserInterface> ReadOperations::getLocalConfig(const string
|
|||
|
||||
void ReadOperations::getConfig(bool getTemplate,
|
||||
Config_t &config)
|
||||
{
|
||||
getNamedConfig(m_configName, getTemplate, config);
|
||||
}
|
||||
|
||||
void ReadOperations::getNamedConfig(const std::string &configName,
|
||||
bool getTemplate,
|
||||
Config_t &config)
|
||||
{
|
||||
map<string, string> localConfigs;
|
||||
boost::shared_ptr<SyncConfig> dbusConfig;
|
||||
|
@ -107,7 +114,7 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
string peer, context;
|
||||
|
||||
boost::shared_ptr<SyncConfig::TemplateDescription> peerTemplate =
|
||||
m_server.getPeerTempl(m_configName);
|
||||
m_server.getPeerTempl(configName);
|
||||
if(peerTemplate) {
|
||||
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(peerTemplate->m_templateId),
|
||||
peer, context);
|
||||
|
@ -137,13 +144,13 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
syncURL = "obex-bt://";
|
||||
syncURL += peerTemplate->m_deviceId;
|
||||
} else {
|
||||
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
|
||||
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(configName),
|
||||
peer, context);
|
||||
dbusConfig = SyncConfig::createPeerTemplate(peer);
|
||||
}
|
||||
|
||||
if(!dbusConfig.get()) {
|
||||
SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + m_configName + "' found");
|
||||
SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + configName + "' found");
|
||||
}
|
||||
|
||||
// use the shared properties from the right context as filter
|
||||
|
@ -166,11 +173,11 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
}
|
||||
syncConfig = dbusConfig.get();
|
||||
} else {
|
||||
dbusUI = getLocalConfig(m_configName);
|
||||
dbusUI = getLocalConfig(configName);
|
||||
//try to check password and read password from gnome keyring if possible
|
||||
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
|
||||
BOOST_FOREACH(const ConfigProperty *prop, registry) {
|
||||
prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties());
|
||||
prop->checkPassword(*dbusUI, configName, *dbusUI->getProperties());
|
||||
}
|
||||
list<string> configuredSources = dbusUI->getSyncSources();
|
||||
BOOST_FOREACH(const string &sourceName, configuredSources) {
|
||||
|
@ -178,7 +185,7 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
SyncSourceNodes sourceNodes = dbusUI->getSyncSourceNodes(sourceName);
|
||||
|
||||
BOOST_FOREACH(const ConfigProperty *prop, registry) {
|
||||
prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties(),
|
||||
prop->checkPassword(*dbusUI, configName, *dbusUI->getProperties(),
|
||||
sourceName, sourceNodes.getProperties());
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +219,7 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
localConfigs.insert(make_pair("ConsumerReady", "1"));
|
||||
}
|
||||
|
||||
// insert 'configName' of the chosen config (m_configName is not normalized)
|
||||
// insert 'configName' of the chosen config (configName is not normalized)
|
||||
localConfigs.insert(pair<string, string>("configName", syncConfig->getConfigName()));
|
||||
|
||||
config.insert(pair<string,map<string, string> >("", localConfigs));
|
||||
|
|
|
@ -61,6 +61,11 @@ public:
|
|||
void getConfig(bool getTemplate,
|
||||
Config_t &config);
|
||||
|
||||
/** implementation of D-Bus GetNamedConfig() for configuration named in parameter */
|
||||
void getNamedConfig(const std::string &configName,
|
||||
bool getTemplate,
|
||||
Config_t &config);
|
||||
|
||||
/** implementation of D-Bus GetReports() for m_configName as server configuration */
|
||||
void getReports(uint32_t start, uint32_t count,
|
||||
Reports_t &reports);
|
||||
|
|
|
@ -73,6 +73,7 @@ vector<string> Server::getCapabilities()
|
|||
|
||||
capabilities.push_back("ConfigChanged");
|
||||
capabilities.push_back("GetConfigName");
|
||||
capabilities.push_back("NamedConfig");
|
||||
capabilities.push_back("Notifications");
|
||||
capabilities.push_back("Version");
|
||||
capabilities.push_back("SessionFlags");
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include "client.h"
|
||||
#include "dbus-sync.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace GDBusCXX;
|
||||
|
||||
SE_BEGIN_CXX
|
||||
|
@ -114,8 +116,16 @@ static void setSyncFilters(const ReadOperations::Config_t &config,FilterConfigNo
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::setConfig(bool update, bool temporary,
|
||||
const ReadOperations::Config_t &config)
|
||||
{
|
||||
setNamedConfig(m_configName, update, temporary, config);
|
||||
}
|
||||
|
||||
void Session::setNamedConfig(const std::string &configName,
|
||||
bool update, bool temporary,
|
||||
const ReadOperations::Config_t &config)
|
||||
{
|
||||
if (!m_active) {
|
||||
SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
|
||||
|
@ -124,11 +134,30 @@ void Session::setConfig(bool update, bool temporary,
|
|||
string msg = StringPrintf("%s started, cannot change configuration at this time", runOpToString(m_runOperation).c_str());
|
||||
SE_THROW_EXCEPTION(InvalidCall, msg);
|
||||
}
|
||||
// avoid the check if effect is the same as setConfig()
|
||||
if (m_configName != configName) {
|
||||
bool found = false;
|
||||
BOOST_FOREACH(const std::string &flag, m_flags) {
|
||||
if (boost::iequals(flag, "all-configs")) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
SE_THROW_EXCEPTION(InvalidCall,
|
||||
"SetNameConfig() only allowed in 'all-configs' sessions");
|
||||
}
|
||||
|
||||
m_server.getPresenceStatus().updateConfigPeers (m_configName, config);
|
||||
if (temporary) {
|
||||
SE_THROW_EXCEPTION(InvalidCall,
|
||||
"SetNameConfig() with temporary config change only supported for config named when starting the session");
|
||||
}
|
||||
}
|
||||
|
||||
m_server.getPresenceStatus().updateConfigPeers (configName, config);
|
||||
/** check whether we need remove the entire configuration */
|
||||
if(!update && !temporary && config.empty()) {
|
||||
boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(getConfigName()));
|
||||
boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(configName));
|
||||
if(syncConfig.get()) {
|
||||
syncConfig->remove();
|
||||
m_setConfig = true;
|
||||
|
@ -166,10 +195,10 @@ void Session::setConfig(bool update, bool temporary,
|
|||
m_tempConfig = true;
|
||||
} else {
|
||||
/* need to save configurations */
|
||||
boost::shared_ptr<SyncConfig> from(new SyncConfig(getConfigName()));
|
||||
boost::shared_ptr<SyncConfig> from(new SyncConfig(configName));
|
||||
/* if it is not clear mode and config does not exist, an error throws */
|
||||
if(update && !from->exists()) {
|
||||
SE_THROW_EXCEPTION(NoSuchConfig, "The configuration '" + getConfigName() + "' doesn't exist" );
|
||||
SE_THROW_EXCEPTION(NoSuchConfig, "The configuration '" + configName + "' doesn't exist" );
|
||||
}
|
||||
if(!update) {
|
||||
list<string> sources = from->getSyncSources();
|
||||
|
@ -202,7 +231,7 @@ void Session::setConfig(bool update, bool temporary,
|
|||
for ( it = sourceFilters.begin(); it != sourceFilters.end(); it++ ) {
|
||||
from->setConfigFilter(false, it->first, it->second);
|
||||
}
|
||||
boost::shared_ptr<DBusSync> syncConfig(new DBusSync(getConfigName(), *this));
|
||||
boost::shared_ptr<DBusSync> syncConfig(new DBusSync(configName, *this));
|
||||
syncConfig->prepareConfigForWrite();
|
||||
syncConfig->copy(*from, NULL);
|
||||
|
||||
|
@ -432,7 +461,9 @@ Session::Session(Server &server,
|
|||
add(this, &Session::getNormalConfigName, "GetConfigName");
|
||||
add(static_cast<ReadOperations *>(this), &ReadOperations::getConfigs, "GetConfigs");
|
||||
add(static_cast<ReadOperations *>(this), &ReadOperations::getConfig, "GetConfig");
|
||||
add(static_cast<ReadOperations *>(this), &ReadOperations::getNamedConfig, "GetNamedConfig");
|
||||
add(this, &Session::setConfig, "SetConfig");
|
||||
add(this, &Session::setNamedConfig, "SetNamedConfig");
|
||||
add(static_cast<ReadOperations *>(this), &ReadOperations::getReports, "GetReports");
|
||||
add(static_cast<ReadOperations *>(this), &ReadOperations::checkSource, "CheckSource");
|
||||
add(static_cast<ReadOperations *>(this), &ReadOperations::getDatabases, "GetDatabases");
|
||||
|
|
|
@ -367,6 +367,11 @@ public:
|
|||
void setConfig(bool update, bool temporary,
|
||||
const ReadOperations::Config_t &config);
|
||||
|
||||
/** Session.SetNamedConfig() */
|
||||
void setNamedConfig(const std::string &configName,
|
||||
bool update, bool temporary,
|
||||
const ReadOperations::Config_t &config);
|
||||
|
||||
typedef StringMap SourceModes_t;
|
||||
/** Session.Sync() */
|
||||
void sync(const std::string &mode, const SourceModes_t &source_modes);
|
||||
|
|
|
@ -593,9 +593,9 @@ class DBusUtil(Timeout):
|
|||
DBusUtil.quit_events = []
|
||||
return (sessionpath, session)
|
||||
|
||||
def setUpSession(self, config):
|
||||
def setUpSession(self, config, flags=[]):
|
||||
"""stores ready session in self.sessionpath and self.session"""
|
||||
self.sessionpath, self.session = self.createSession(config, True)
|
||||
self.sessionpath, self.session = self.createSession(config, True, flags)
|
||||
|
||||
def progressChanged(self, *args):
|
||||
'''subclasses override this method to write specified callbacks for ProgressChanged signals
|
||||
|
@ -796,7 +796,7 @@ class TestDBusServer(unittest.TestCase, DBusUtil):
|
|||
"""TestDBusServer.testCapabilities - Server.Capabilities()"""
|
||||
capabilities = self.server.GetCapabilities()
|
||||
capabilities.sort()
|
||||
self.assertEqual(capabilities, ['ConfigChanged', 'DatabaseProperties', 'GetConfigName', 'Notifications', 'SessionAttach', 'SessionFlags', 'Version'])
|
||||
self.assertEqual(capabilities, ['ConfigChanged', 'DatabaseProperties', 'GetConfigName', 'NamedConfig', 'Notifications', 'SessionAttach', 'SessionFlags', 'Version'])
|
||||
|
||||
def testVersions(self):
|
||||
"""TestDBusServer.testVersions - Server.GetVersions()"""
|
||||
|
@ -1008,6 +1008,57 @@ class TestDBusServerTerm(unittest.TestCase, DBusUtil):
|
|||
else:
|
||||
self.fail("no exception thrown")
|
||||
|
||||
class TestNamedConfig(unittest.TestCase, DBusUtil):
|
||||
"""Tests for Set/GetNamedConfig"""
|
||||
|
||||
def setUp(self):
|
||||
self.setUpServer()
|
||||
|
||||
def run(self, result):
|
||||
self.runTest(result)
|
||||
|
||||
def testSetNamedConfigError(self):
|
||||
"""TestDBusSession.testSetNamedConfigError - SetNamedConfig() only allowed in 'all-configs' sessions"""
|
||||
self.setUpSession("")
|
||||
try:
|
||||
self.session.SetNamedConfig("foobar", False, False, {})
|
||||
except dbus.DBusException, ex:
|
||||
self.assertEqual(str(ex),
|
||||
"org.syncevolution.InvalidCall: SetNameConfig() only allowed in 'all-configs' sessions")
|
||||
else:
|
||||
self.fail("no exception thrown")
|
||||
|
||||
def testSetNamedConfigErrorTemporary(self):
|
||||
"""TestDBusSession.testSetNamedConfigErrorTemporary - SetNamedConfig() only implemented for session config"""
|
||||
self.setUpSession("foo", [ "all-configs" ])
|
||||
try:
|
||||
self.session.SetNamedConfig("foobar", False, True, {})
|
||||
except dbus.DBusException, ex:
|
||||
self.assertEqual(str(ex),
|
||||
"org.syncevolution.InvalidCall: SetNameConfig() with temporary config change only supported for config named when starting the session")
|
||||
else:
|
||||
self.fail("no exception thrown")
|
||||
self.session.Detach()
|
||||
|
||||
self.setUpSession("")
|
||||
self.session.SetNamedConfig("", False, True, {})
|
||||
|
||||
def testSetNamedConfig(self):
|
||||
"""TestDBusSession.testSetNamedConfig - create two different configs in one session"""
|
||||
self.setUpSession("", [ "all-configs" ])
|
||||
|
||||
fooConfig = {"": {"username": "foo", "configName": "foo"}}
|
||||
barConfig = {"": {"username": "bar", "configName": "bar"}}
|
||||
|
||||
self.session.SetNamedConfig("foo", False, False, fooConfig)
|
||||
self.session.SetNamedConfig("bar", False, False, barConfig)
|
||||
|
||||
self.assertEqual(fooConfig, self.server.GetConfig("foo", False))
|
||||
self.assertEqual(barConfig, self.server.GetConfig("bar", False))
|
||||
|
||||
self.assertEqual(fooConfig, self.session.GetNamedConfig("foo", False))
|
||||
self.assertEqual(barConfig, self.session.GetNamedConfig("bar", False))
|
||||
|
||||
|
||||
class Connman (dbus.service.Object):
|
||||
count = 0
|
||||
|
|
Loading…
Reference in New Issue