From ee074c7b460789e8fefb0d127ba217529fb88e2c Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 4 May 2018 12:43:33 +0200 Subject: Add Todo support in caldav synchronizer --- examples/caldavresource/caldavresource.cpp | 80 ++++++++++++------- examples/caldavresource/tests/caldavtest.cpp | 111 +++++++++++++++++++++++++-- 2 files changed, 155 insertions(+), 36 deletions(-) diff --git a/examples/caldavresource/caldavresource.cpp b/examples/caldavresource/caldavresource.cpp index cdd224f..e28fbc5 100644 --- a/examples/caldavresource/caldavresource.cpp +++ b/examples/caldavresource/caldavresource.cpp @@ -78,21 +78,27 @@ protected: switch (incidence->type()) { case Type::TypeEvent: { - auto remoteEvent = dynamic_cast(*incidence); - Event localEvent; localEvent.setIcal(ical); localEvent.setCalendar(calendarLocalId); - SinkTrace() << "Found an event:" << localEvent.getSummary() << "with id:" << rid; + SinkTrace() << "Found an event with id:" << rid; createOrModify(ENTITY_TYPE_EVENT, rid, localEvent, /* mergeCriteria = */ QHash{}); break; } - case Type::TypeTodo: - SinkWarning() << "Unimplemented add of a 'Todo' item in the Store"; + case Type::TypeTodo: { + Todo localTodo; + localTodo.setIcal(ical); + localTodo.setCalendar(calendarLocalId); + + SinkTrace() << "Found a Todo with id:" << rid; + + createOrModify(ENTITY_TYPE_TODO, rid, localTodo, + /* mergeCriteria = */ QHash{}); break; + } case Type::TypeJournal: SinkWarning() << "Unimplemented add of a 'Journal' item in the Store"; break; @@ -112,58 +118,74 @@ protected: return syncStore().resolveRemoteId(ENTITY_TYPE_CALENDAR, resourceID(calendar)); } - KAsync::Job replay(const Event &event, Sink::Operation operation, - const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE + template + KAsync::Job replayItem(const Item &localItem, Sink::Operation operation, + const QByteArray &oldRemoteId, const QList &changedProperties, + const QByteArray &entityType) { - SinkLog() << "Replaying event"; + SinkLog() << "Replaying" << entityType; - KDAV2::DavItem item; + KDAV2::DavItem remoteItem; switch (operation) { case Sink::Operation_Creation: { - auto rawIcal = event.getIcal(); - if(rawIcal == "") { - return KAsync::error("No ICal in event for creation replay"); + auto rawIcal = localItem.getIcal(); + if (rawIcal == "") { + return KAsync::error("No ICal in item for creation replay"); } - auto collectionId = syncStore().resolveLocalId(ENTITY_TYPE_CALENDAR, event.getCalendar()); + auto collectionId = syncStore().resolveLocalId(ENTITY_TYPE_CALENDAR, localItem.getCalendar()); - item.setData(rawIcal); - item.setContentType("text/calendar"); - item.setUrl(urlOf(collectionId, event.getUid())); + remoteItem.setData(rawIcal); + remoteItem.setContentType("text/calendar"); + remoteItem.setUrl(urlOf(collectionId, localItem.getUid())); - SinkLog() << "Creating event:" << event.getSummary(); - return createItem(item).then([item] { return resourceID(item); }); + SinkLog() << "Creating" << entityType << ":" << localItem.getSummary(); + return createItem(remoteItem).then([remoteItem] { return resourceID(remoteItem); }); } case Sink::Operation_Removal: { // We only need the URL in the DAV item for removal - item.setUrl(urlOf(oldRemoteId)); + remoteItem.setUrl(urlOf(oldRemoteId)); - SinkLog() << "Removing event:" << oldRemoteId; - return removeItem(item).then([] { return QByteArray{}; }); + SinkLog() << "Removing" << entityType << ":" << oldRemoteId; + return removeItem(remoteItem).then([] { return QByteArray{}; }); } case Sink::Operation_Modification: - auto rawIcal = event.getIcal(); - if(rawIcal == "") { - return KAsync::error("No ICal in event for modification replay"); + auto rawIcal = localItem.getIcal(); + if (rawIcal == "") { + return KAsync::error("No ICal in item for modification replay"); } - item.setData(rawIcal); - item.setContentType("text/calendar"); - item.setUrl(urlOf(oldRemoteId)); + remoteItem.setData(rawIcal); + remoteItem.setContentType("text/calendar"); + remoteItem.setUrl(urlOf(oldRemoteId)); - SinkLog() << "Modifying event:" << event.getSummary(); + SinkLog() << "Modifying" << entityType << ":" << localItem.getSummary(); // It would be nice to check that the URL of the item hasn't // changed and move he item if it did, but since the URL is // pretty much arbitrary, whoe does that anyway? - return modifyItem(item).then([oldRemoteId] { return oldRemoteId; }); + return modifyItem(remoteItem).then([oldRemoteId] { return oldRemoteId; }); } } + KAsync::Job replay(const Event &event, Sink::Operation operation, + const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE + { + return replayItem(event, operation, oldRemoteId, changedProperties, ENTITY_TYPE_EVENT); + } + + KAsync::Job replay(const Todo &todo, Sink::Operation operation, + const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE + { + return replayItem(todo, operation, oldRemoteId, changedProperties, ENTITY_TYPE_TODO); + } + KAsync::Job replay(const Calendar &calendar, Sink::Operation operation, const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE { + SinkLog() << "Replaying calendar"; + switch (operation) { case Sink::Operation_Creation: SinkWarning() << "Unimplemented replay of calendar creation"; diff --git a/examples/caldavresource/tests/caldavtest.cpp b/examples/caldavresource/tests/caldavtest.cpp index 4dc35f0..1caf19f 100644 --- a/examples/caldavresource/tests/caldavtest.cpp +++ b/examples/caldavresource/tests/caldavtest.cpp @@ -22,13 +22,15 @@ using Sink::ApplicationDomain::Calendar; using Sink::ApplicationDomain::DummyResource; using Sink::ApplicationDomain::Event; +using Sink::ApplicationDomain::Todo; using Sink::ApplicationDomain::SinkResource; class CalDavTest : public QObject { Q_OBJECT - // This test assumes a calendar MyCalendar with one event in it. + // This test assumes a calendar MyCalendar with one event and one todo in + // it. const QString baseUrl = "http://localhost/dav/calendars/users/doe"; const QString username = "doe"; @@ -47,6 +49,7 @@ class CalDavTest : public QObject QByteArray mResourceInstanceIdentifier; QString addedEventUid; + QString addedTodoUid; private slots: @@ -74,6 +77,7 @@ private slots: VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); // Check in the logs that it doesn't synchronize events again because same CTag VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); + VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); } void testSyncCalEmpty() @@ -83,7 +87,11 @@ private slots: auto eventJob = Sink::Store::fetchAll(Sink::Query().request()) .then([](const QList &events) { QCOMPARE(events.size(), 1); }); + auto todoJob = Sink::Store::fetchAll(Sink::Query().request()) + .then([](const QList &todos) { QCOMPARE(todos.size(), 1); }); + VERIFYEXEC(eventJob); + VERIFYEXEC(todoJob); auto calendarJob = Sink::Store::fetchAll(Sink::Query().request()) .then([](const QList &calendars) { @@ -136,6 +144,44 @@ private slots: VERIFYEXEC(verifyEventJob); } + void testAddTodo() + { + VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); + VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); + + auto job = Sink::Store::fetchOne({}).exec(); + job.waitForFinished(); + QVERIFY2(!job.errorCode(), "Fetching Calendar failed"); + auto calendar = job.value(); + + auto todo = QSharedPointer::create(); + todo->setSummary("Hello"); + todo->setDtStart(QDateTime::currentDateTime()); + todo->setCreated(QDateTime::currentDateTime()); + addedTodoUid = QUuid::createUuid().toString(); + todo->setUid(addedTodoUid); + + auto ical = KCalCore::ICalFormat().toICalString(todo); + Todo sinkTodo(mResourceInstanceIdentifier); + sinkTodo.setIcal(ical.toUtf8()); + sinkTodo.setCalendar(calendar); + + SinkLog() << "Adding todo"; + VERIFYEXEC(Sink::Store::create(sinkTodo)); + VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); + + auto verifyTodoCountJob = + Sink::Store::fetchAll(Sink::Query().request()).then([](const QList &todos) { + QCOMPARE(todos.size(), 2); + }); + VERIFYEXEC(verifyTodoCountJob); + + auto verifyTodoJob = + Sink::Store::fetchOne(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))) + .then([](const Todo &todo) { QCOMPARE(todo.getSummary(), "Hello"); }); + VERIFYEXEC(verifyTodoJob); + } + void testModifyEvent() { VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); @@ -173,6 +219,43 @@ private slots: VERIFYEXEC(verifyEventJob); } + void testModifyTodo() + { + VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); + VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); + + auto job = Sink::Store::fetchOne( + Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))) + .exec(); + job.waitForFinished(); + QVERIFY2(!job.errorCode(), "Fetching Todo failed"); + auto todo = job.value(); + + auto incidence = KCalCore::ICalFormat().readIncidence(todo.getIcal()); + auto caltodo = incidence.dynamicCast(); + QVERIFY2(caltodo, "Cannot convert to KCalCore todo"); + + caltodo->setSummary("Hello World!"); + auto dummy = QSharedPointer(caltodo); + auto newical = KCalCore::ICalFormat().toICalString(dummy); + + todo.setIcal(newical.toUtf8()); + + VERIFYEXEC(Sink::Store::modify(todo)); + + VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); + VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); + + auto verifyTodoCountJob = Sink::Store::fetchAll({}).then( + [](const QList &todos) { QCOMPARE(todos.size(), 2); }); + VERIFYEXEC(verifyTodoCountJob); + + auto verifyTodoJob = + Sink::Store::fetchOne(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))) + .then([](const Todo &todo) { QCOMPARE(todo.getSummary(), "Hello World!"); }); + VERIFYEXEC(verifyTodoJob); + } + void testSneakyModifyEvent() { VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); @@ -180,7 +263,6 @@ private slots: // Change the item without sink's knowledge { - qWarning() << 1; auto collection = ([this]() -> KDAV2::DavCollection { QUrl url(baseUrl); url.setUserName(username); @@ -212,22 +294,17 @@ private slots: return itemFetchJob.item(); })(); - qWarning() << 3; auto incidence = KCalCore::ICalFormat().readIncidence(davitem.data()); auto calevent = incidence.dynamicCast(); QVERIFY2(calevent, "Cannot convert to KCalCore event"); - qWarning() << 4; calevent->setSummary("Manual Hello World!"); auto newical = KCalCore::ICalFormat().toICalString(calevent); - qWarning() << 5; davitem.setData(newical.toUtf8()); KDAV2::DavItemModifyJob itemModifyJob(davitem); itemModifyJob.exec(); QVERIFY2(itemModifyJob.error() == 0, "Cannot modify item"); - - qWarning() << 6; } // Try to change the item with sink @@ -274,6 +351,26 @@ private slots: [](const QList &events) { QCOMPARE(events.size(), 1); }); VERIFYEXEC(verifyEventCountJob); } + + void testRemoveTodo() + { + VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); + VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); + + auto job = Sink::Store::fetchOne( + Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))) + .exec(); + job.waitForFinished(); + QVERIFY2(!job.errorCode(), "Fetching Todo failed"); + auto todo = job.value(); + + VERIFYEXEC(Sink::Store::remove(todo)); + VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); + + auto verifyTodoCountJob = Sink::Store::fetchAll({}).then( + [](const QList &todos) { QCOMPARE(todos.size(), 1); }); + VERIFYEXEC(verifyTodoCountJob); + } }; QTEST_MAIN(CalDavTest) -- cgit v1.2.3