From 90a8d33f7c17c802730fd9b978db0e32d28a7dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Nicole?= Date: Sun, 6 May 2018 17:42:37 +0200 Subject: Implement Todo entity type Summary: Some notes: - Needed to specialize some flatbuffers related functions for serializing QStringList and int - Removed useless qWarnings in caldav test - Rename EventSynchronizer -> CalDAVSynchronizer since it also synchronizes Calendars and Todos (and more to come!) Reviewers: cmollekopf Tags: #sink Differential Revision: https://phabricator.kde.org/D12695 --- examples/caldavresource/caldavresource.cpp | 105 ++++++++++++++++--------- examples/caldavresource/tests/caldavtest.cpp | 111 +++++++++++++++++++++++++-- 2 files changed, 173 insertions(+), 43 deletions(-) (limited to 'examples/caldavresource') diff --git a/examples/caldavresource/caldavresource.cpp b/examples/caldavresource/caldavresource.cpp index d9f8c69..d33f625 100644 --- a/examples/caldavresource/caldavresource.cpp +++ b/examples/caldavresource/caldavresource.cpp @@ -25,25 +25,29 @@ #include "applicationdomaintype.h" #include "domainadaptor.h" #include "eventpreprocessor.h" +#include "todopreprocessor.h" #include "facade.h" #include "facadefactory.h" #include #define ENTITY_TYPE_EVENT "event" +#define ENTITY_TYPE_TODO "todo" #define ENTITY_TYPE_CALENDAR "calendar" using Sink::ApplicationDomain::getTypeName; -class EventSynchronizer : public WebDavSynchronizer +class CalDAVSynchronizer : public WebDavSynchronizer { using Event = Sink::ApplicationDomain::Event; + using Todo = Sink::ApplicationDomain::Todo; using Calendar = Sink::ApplicationDomain::Calendar; public: - explicit EventSynchronizer(const Sink::ResourceContext &context) + explicit CalDAVSynchronizer(const Sink::ResourceContext &context) : WebDavSynchronizer(context, KDAV2::CalDav, getTypeName(), getTypeName()) - {} + { + } protected: void updateLocalCollections(KDAV2::DavCollection::List calendarList) Q_DECL_OVERRIDE @@ -74,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; @@ -108,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"; @@ -180,19 +206,23 @@ protected: CalDavResource::CalDavResource(const Sink::ResourceContext &context) : Sink::GenericResource(context) { - auto synchronizer = QSharedPointer::create(context); + auto synchronizer = QSharedPointer::create(context); setupSynchronizer(synchronizer); - setupPreprocessors(ENTITY_TYPE_EVENT, QVector() << new EventPropertyExtractor); + setupPreprocessors(ENTITY_TYPE_EVENT, QVector() << new EventPropertyExtractor); + setupPreprocessors(ENTITY_TYPE_TODO, QVector() << new TodoPropertyExtractor); } CalDavResourceFactory::CalDavResourceFactory(QObject *parent) : Sink::ResourceFactory(parent, { - Sink::ApplicationDomain::ResourceCapabilities::Event::event, Sink::ApplicationDomain::ResourceCapabilities::Event::calendar, + Sink::ApplicationDomain::ResourceCapabilities::Event::event, Sink::ApplicationDomain::ResourceCapabilities::Event::storage, + Sink::ApplicationDomain::ResourceCapabilities::Todo::todo, + Sink::ApplicationDomain::ResourceCapabilities::Todo::storage, }) -{} +{ +} Sink::Resource *CalDavResourceFactory::createResource(const Sink::ResourceContext &context) { @@ -201,10 +231,12 @@ Sink::Resource *CalDavResourceFactory::createResource(const Sink::ResourceContex using Sink::ApplicationDomain::Calendar; using Sink::ApplicationDomain::Event; +using Sink::ApplicationDomain::Todo; void CalDavResourceFactory::registerFacades(const QByteArray &resourceName, Sink::FacadeFactory &factory) { factory.registerFacade>(resourceName); + factory.registerFacade>(resourceName); factory.registerFacade>(resourceName); } @@ -213,6 +245,7 @@ void CalDavResourceFactory::registerAdaptorFactories( const QByteArray &resourceName, Sink::AdaptorFactoryRegistry ®istry) { registry.registerFactory>(resourceName); + registry.registerFactory>(resourceName); registry.registerFactory>(resourceName); } diff --git a/examples/caldavresource/tests/caldavtest.cpp b/examples/caldavresource/tests/caldavtest.cpp index f9920d9..9609fa0 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