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:
parent
a4cc410b31
commit
849c5408e5
23
README.rst
23
README.rst
|
@ -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
|
||||
|
|
|
@ -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 ®istry(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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue