TestingSyncSource+CalDAV: added "delete all" API call

During testing, removing all items is done via a special call in
TestingSyncSource. It used to be an utility method which fell back to
the normal SyncSource API.

This patch changes several things
- Reversed order in which items are deleted in that utility method,
  because removing children (= longer IDs) first tends to be supported
  better by servers (bug in CalDAV server, but still...).
- Allow backends to implement their own removeAllItems().
- Implement that in CalDAV + MapSyncSource as removing the merged
  items directly, instead of using a sequence of PUT+final DELETE.

Found while testing with Bedework CalDAV server. Makes testing more
robust and efficient.
This commit is contained in:
Patrick Ohly 2011-10-04 09:55:18 +02:00
parent a7f7c8eacf
commit 765ed0f833
6 changed files with 76 additions and 5 deletions

View File

@ -858,6 +858,48 @@ std::string CalDAVSource::removeSubItem(const string &davLUID, const std::string
}
}
void CalDAVSource::removeMergedItem(const std::string &davLUID)
{
EventCache::iterator it = m_cache.find(davLUID);
if (it == m_cache.end()) {
// gone already, no need to do anything
SE_LOG_DEBUG(this, NULL, "%s: ignoring request to delete non-existent item",
davLUID.c_str());
return;
}
// use item as it is, load only if it is not going to be removed entirely
Event &event = *it->second;
// remove entire merged item, nothing will be left after removal
try {
removeItem(event.m_DAVluid);
} catch (const TransportStatusException &ex) {
if (ex.syncMLStatus() == 404) {
// Someone must have created a detached recurrence on
// the server without the master event - or the
// item was already removed while the sync ran.
// Let's log the problem and ignore it.
Exception::log();
} else if (ex.syncMLStatus() == 409 &&
strstr(ex.what(), "Can't delete a recurring event")) {
// Google CalDAV:
// HTTP/1.1 409 Can't delete a recurring event except on its organizer's calendar
//
// Workaround: use the workarounds from removeSubItem()
std::set<std::string> subids = event.m_subids;
for (std::set<std::string>::reverse_iterator it = subids.rbegin();
it != subids.rend();
++it) {
removeSubItem(davLUID, *it);
}
} else {
throw;
}
}
m_cache.erase(davLUID);
}
void CalDAVSource::flushItem(const string &davLUID)
{
// TODO: currently we always flush immediately, so no need to send data here

View File

@ -42,6 +42,7 @@ class CalDAVSource : public WebDAVSource,
const std::string &item);
virtual void readSubItem(const std::string &uid, const std::string &subid, std::string &item);
virtual std::string removeSubItem(const string &uid, const std::string &subid);
virtual void removeMergedItem(const std::string &luid);
virtual void flushItem(const string &uid);
virtual std::string getSubDescription(const string &uid, const string &subid);

View File

@ -351,4 +351,13 @@ std::pair<std::string, std::string> MapSyncSource::splitLUID(const std::string &
StringEscape MapSyncSource::m_escape('%', "/");
void MapSyncSource::removeAllItems()
{
BOOST_FOREACH(const SubRevisionMap_t::value_type &entry,
m_revisions) {
m_sub->removeMergedItem(entry.first);
}
m_revisions.clear();
}
SE_END_CXX

View File

@ -166,6 +166,11 @@ class SubSyncSource : virtual public SyncSourceBase
*/
virtual std::string removeSubItem(const string &mainid, const std::string &subid) = 0;
/**
* Remove all sub-items belonging to mainid.
*/
virtual void removeMergedItem(const std::string &mainid) = 0;
/**
* Called whenever this class thinks that the item may no longer be
* needed. Might be wrong...
@ -265,6 +270,9 @@ class MapSyncSource :
virtual std::string getDescription(sysync::KeyH aItemKey) { return dynamic_cast<SyncSourceLogging &>(*m_sub).getDescription(aItemKey); }
virtual std::string getDescription(const string &luid);
/* TestingSyncSource */
virtual void removeAllItems();
private:
boost::shared_ptr<SubSyncSource> m_sub;
/** escape / in uid with %2F, so that splitMainIDValue() and splitLUID() can use / as separator */

View File

@ -1388,5 +1388,20 @@ void SyncSourceBlob::init(SyncSource::Operations &ops,
_1, _2);
}
void TestingSyncSource::removeAllItems()
{
// remove longest luids first:
// for luid=UID[+RECURRENCE-ID] that will
// remove children from a merged event first,
// which is better supported by certain servers
Items_t items = getAllItems();
for (Items_t::reverse_iterator it = items.rbegin();
it != items.rend();
++it) {
deleteItem(*it);
}
}
SE_END_CXX

View File

@ -2081,11 +2081,7 @@ class TestingSyncSource : public SyncSource,
virtual SourceType getSourceType() const { return SyncSourceConfig::getSourceType(); }
void removeAllItems() {
BOOST_FOREACH(const string &luid, getAllItems()) {
deleteItem(luid);
}
}
virtual void removeAllItems();
};