command line: implement --create/remove-database

Creating a database is only possible with a chosen name. The UID is
chosen automatically by the storage. A new property would be needed
to also specify the UID.

There are no command line tests for the new functionality because
support for it in backends is limited.
This commit is contained in:
Patrick Ohly 2012-09-13 18:02:07 +02:00
parent a4cc410b31
commit 849c5408e5
4 changed files with 111 additions and 24 deletions

View File

@ -13,8 +13,8 @@ synchronize personal information management data
SYNOPSIS
========
List databases:
syncevolution --print-databases [<properties>] [<config> <source>]
List and manipulate databases:
syncevolution --print-databases|--create-database|--remove-database [<properties>] [<config> <source>]
Show information about configuration(s):
syncevolution --print-servers|--print-configs|--print-peers
@ -317,6 +317,25 @@ file backend synchronizes directories with one file per item and
always needs an explicit ``database`` property because it cannot guess
which directory it is meant to use. ::
syncevolution --create-database [<properties>] [<config> <source>]
Creates a new database for the selected ``backend``, using the
information given in the ``database`` property. As with
``--print-databases``, it is possible to give the properties directly
without configuring a source first.
The interpretation of the ``database`` property depends on the
backend. Not all backends support this operation.
The EDS backend uses the value of the ``database`` as name of the new
database and assigns a unique URI automatically. ::
syncevolution --remove-database [<properties>] [<config> <source>]
Looks up the database based on the ``database`` property (depending
on the backend, both name and a URI are valid), then deletes the data.
Note that source configurations using the database are not removed. ::
syncevolution <config>
Without the optional list of sources, all sources which are enabled in

View File

@ -204,6 +204,12 @@ bool Cmdline::parse(vector<string> &parsed)
} else if(boost::iequals(m_argv[opt], "--print-databases")) {
operations.push_back(m_argv[opt]);
m_printDatabases = true;
} else if(boost::iequals(m_argv[opt], "--create-database")) {
operations.push_back(m_argv[opt]);
m_createDatabase = true;
} else if(boost::iequals(m_argv[opt], "--remove-database")) {
operations.push_back(m_argv[opt]);
m_removeDatabase = true;
} else if(boost::iequals(m_argv[opt], "--print-servers") ||
boost::iequals(m_argv[opt], "--print-peers") ||
boost::iequals(m_argv[opt], "--print-configs")) {
@ -403,6 +409,8 @@ bool Cmdline::isSync()
m_printTemplates || m_dontrun ||
m_argc == 1 || (m_useDaemon.wasSet() && m_argc == 2) ||
m_printDatabases ||
m_createDatabase ||
m_removeDatabase ||
m_printConfig || m_remove ||
(m_server == "" && m_argc > 1) ||
m_configure || m_migrate ||
@ -704,8 +712,8 @@ bool Cmdline::run() {
}
} else if (m_dontrun) {
// user asked for information
} else if (m_printDatabases) {
// list databases
} else if (m_printDatabases || m_createDatabase || m_removeDatabase) {
// manipulate databases
const SourceRegistry &registry(SyncSource::getSourceRegistry());
boost::shared_ptr<SyncSourceNodes> nodes;
std::string header;
@ -714,6 +722,11 @@ bool Cmdline::run() {
std::string sourceName;
FilterConfigNode::ConfigFilter::const_iterator backend;
void (Cmdline::*operation)(SyncSource *, const std::string &) =
m_printDatabases ? &Cmdline::listDatabases :
m_createDatabase ? &Cmdline::createDatabase :
&Cmdline::removeDatabase;
if (!m_server.empty()) {
// list for specific backend chosen via config
if (m_sources.size() != 1) {
@ -754,6 +767,7 @@ bool Cmdline::run() {
SyncSourceParams params("list", *nodes, context);
if (!m_server.empty() || backend != sourceFilter.end()) {
// list for specific backend
params.m_name = sourceName;
auto_ptr<SyncSource> source(SyncSource::createSource(params, false, NULL));
if (source.get() != NULL) {
if (!m_server.empty() && nodes) {
@ -761,10 +775,9 @@ bool Cmdline::run() {
checkSyncPasswords(*context);
checkSourcePasswords(*context, sourceName, *nodes);
}
listSources(*source, header);
SE_LOG_SHOW(NULL, NULL, "\n");
(this->*operation)(source.get(), header);
} else {
SE_LOG_SHOW(NULL, NULL, "%s:\n cannot list databases", header.c_str());
SE_LOG_SHOW(NULL, NULL, "%s:\n cannot access databases", header.c_str());
}
} else {
// list for all backends
@ -775,16 +788,14 @@ bool Cmdline::run() {
nodes->getProperties()->setProperty("backend", type.m_backend);
std::string header = boost::join(alias, " = ");
try {
// The name is used in error messages. We
// don't have a source name, so let's fall
// back to the backend instead.
params.m_name = type.m_backend;
auto_ptr<SyncSource> source(SyncSource::createSource(params, false));
if (!source.get()) {
// silently skip backends like the "file" backend which do not support
// listing databases and return NULL unless configured properly
} else {
listSources(*source, header);
SE_LOG_SHOW(NULL, NULL, "\n");
}
(this->*operation)(source.get(), header);
} catch (...) {
SE_LOG_ERROR(NULL, NULL, "%s:\nlisting databases failed", header.c_str());
SE_LOG_ERROR(NULL, NULL, "%s:\nacessing databases failed", header.c_str());
Exception::handle();
}
}
@ -1997,15 +2008,21 @@ void Cmdline::checkForPeerProps()
}
}
void Cmdline::listSources(SyncSource &syncSource, const string &header)
void Cmdline::listDatabases(SyncSource *source, const string &header)
{
if (!source) {
// silently skip backends like the "file" backend which do not support
// listing databases and return NULL unless configured properly
return;
}
ostringstream out;
out << header << ":\n";
if (syncSource.isInactive()) {
out << "not enabled during compilation or not usable in the current environment\n";
if (source->isInactive()) {
out << source->getBackend() << ": not enabled during compilation or not usable in the current environment\n";
} else {
SyncSource::Databases databases = syncSource.getDatabases();
SyncSource::Databases databases = source->getDatabases();
BOOST_FOREACH(const SyncSource::Database &database, databases) {
out << " " << database.m_name << " (" << database.m_uri << ")";
@ -2016,6 +2033,53 @@ void Cmdline::listSources(SyncSource &syncSource, const string &header)
}
}
SE_LOG_SHOW(NULL, NULL, "%s", out.str().c_str());
SE_LOG_SHOW(NULL, NULL, "\n");
}
void Cmdline::createDatabase(SyncSource *source, const string &header)
{
if (!source) {
SE_THROW(StringPrintf("%s:\ncannot access databases", header.c_str()));
return;
}
// Only the name can be set via the command line. URI is chosen by backend.
InitStateString databaseID = source->getDatabaseID();
if (!databaseID.wasSet()) {
SE_THROW("The 'database' property must be set to the name of the new database");
}
SyncSource::Database database = source->createDatabase(SyncSource::Database(databaseID, ""));
SE_LOG_SHOW(NULL, NULL, "%s: database '%s' (%s) was created.",
header.c_str(),
database.m_name.c_str(),
database.m_uri.c_str());
}
void Cmdline::removeDatabase(SyncSource *source, const string &header)
{
if (!source) {
SE_THROW(StringPrintf("%s:\ncannot access databases", header.c_str()));
return;
}
InitStateString databaseID = source->getDatabaseID();
if (!databaseID.wasSet()) {
SE_THROW("The 'database' property was not set. Cowardly refusing to remove the default database. Set it to the empty string and try again if that was the intention.");
}
// determine URI
source->open();
SyncSource::Database database = source->getDatabase();
if (database.m_uri.empty()) {
SE_THROW(StringPrintf("Cannot determine database from 'database' property value '%s'.",
databaseID.c_str()));
}
source->deleteDatabase(database.m_uri);
SE_LOG_SHOW(NULL, NULL, "%s: database '%s' (%s) was removed.",
header.c_str(),
database.m_name.c_str(),
database.m_uri.c_str());
}
void Cmdline::dumpConfigs(const string &preamble,
@ -4373,7 +4437,7 @@ private:
std::string out = m_out.str();
std::string err = m_err.str();
std::string all = m_all.str();
CPPUNIT_ASSERT(boost::starts_with(out, "List databases:\n"));
CPPUNIT_ASSERT(boost::starts_with(out, "List and manipulate databases:\n"));
CPPUNIT_ASSERT(out.find("\nOptions:\n") == std::string::npos);
CPPUNIT_ASSERT(boost::ends_with(out,
"Remove item(s):\n"

View File

@ -142,6 +142,8 @@ protected:
Bool m_run;
Bool m_migrate;
Bool m_printDatabases;
Bool m_createDatabase;
Bool m_removeDatabase;
Bool m_printServers;
Bool m_printTemplates;
Bool m_printConfig;
@ -244,7 +246,9 @@ protected:
/**
* list all known data sources of a certain type
*/
void listSources(SyncSource &syncSource, const std::string &header);
void listDatabases(SyncSource *source, const std::string &header);
void createDatabase(SyncSource *source, const std::string &header);
void removeDatabase(SyncSource *source, const std::string &header);
void dumpConfigs(const std::string &preamble,
const SyncConfig::ConfigList &servers);

View File

@ -5569,7 +5569,7 @@ spds/sources/todo/config.txt:# evolutionpassword =
def expectUsageError(self, out, err, specific_error):
'''verify a short usage info was produced and specific error
message was printed'''
self.assertTrue(out.startswith("List databases:\n"))
self.assertTrue(out.startswith("List and manipulate databases:\n"))
self.assertEqual(out.find("\nOptions:\n"), -1)
self.assertTrue(out.endswith("Remove item(s):\n" \
" syncevolution --delete-items [--] <config> <source> (<luid> ... | '*')\n\n"))
@ -5703,7 +5703,7 @@ spds/sources/todo/config.txt:# evolutionpassword =
sessionFlags=None,
expectSuccess=False)
self.assertEqualDiff('[ERROR] --foo-bar: unknown parameter\n', stripOutput(err))
self.assertRegexpMatches(out, '^List databases:\n')
self.assertRegexpMatches(out, '^List and manipulate databases:\n')
self.assertEqual(1, code)
# Run command without talking to server, joined streams.
@ -5712,7 +5712,7 @@ spds/sources/todo/config.txt:# evolutionpassword =
expectSuccess=False,
preserveOutputOrder=True)
self.assertEqual(err, None)
self.assertRegexpMatches(stripOutput(out), r'^List databases:\n(.*\n)*\[ERROR\] --foo-bar: unknown parameter\n$')
self.assertRegexpMatches(stripOutput(out), r'^List and manipulate databases:\n(.*\n)*\[ERROR\] --foo-bar: unknown parameter\n$')
self.assertEqual(1, code)
peerMin = self.getPeerMinVersion()