summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2018-05-04 12:43:33 +0200
committerMinijackson <minijackson@riseup.net>2018-05-04 12:43:33 +0200
commitee074c7b460789e8fefb0d127ba217529fb88e2c (patch)
treeb32536cf633ba00893cfb84917469ef8e101a0ce
parent404d4619ccfc2ea014bdec1a26a9c88d1c314227 (diff)
downloadsink-ee074c7b460789e8fefb0d127ba217529fb88e2c.tar.gz
sink-ee074c7b460789e8fefb0d127ba217529fb88e2c.zip
Add Todo support in caldav synchronizer
-rw-r--r--examples/caldavresource/caldavresource.cpp80
-rw-r--r--examples/caldavresource/tests/caldavtest.cpp111
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:
78 78
79 switch (incidence->type()) { 79 switch (incidence->type()) {
80 case Type::TypeEvent: { 80 case Type::TypeEvent: {
81 auto remoteEvent = dynamic_cast<const KCalCore::Event &>(*incidence);
82
83 Event localEvent; 81 Event localEvent;
84 localEvent.setIcal(ical); 82 localEvent.setIcal(ical);
85 localEvent.setCalendar(calendarLocalId); 83 localEvent.setCalendar(calendarLocalId);
86 84
87 SinkTrace() << "Found an event:" << localEvent.getSummary() << "with id:" << rid; 85 SinkTrace() << "Found an event with id:" << rid;
88 86
89 createOrModify(ENTITY_TYPE_EVENT, rid, localEvent, 87 createOrModify(ENTITY_TYPE_EVENT, rid, localEvent,
90 /* mergeCriteria = */ QHash<QByteArray, Sink::Query::Comparator>{}); 88 /* mergeCriteria = */ QHash<QByteArray, Sink::Query::Comparator>{});
91 break; 89 break;
92 } 90 }
93 case Type::TypeTodo: 91 case Type::TypeTodo: {
94 SinkWarning() << "Unimplemented add of a 'Todo' item in the Store"; 92 Todo localTodo;
93 localTodo.setIcal(ical);
94 localTodo.setCalendar(calendarLocalId);
95
96 SinkTrace() << "Found a Todo with id:" << rid;
97
98 createOrModify(ENTITY_TYPE_TODO, rid, localTodo,
99 /* mergeCriteria = */ QHash<QByteArray, Sink::Query::Comparator>{});
95 break; 100 break;
101 }
96 case Type::TypeJournal: 102 case Type::TypeJournal:
97 SinkWarning() << "Unimplemented add of a 'Journal' item in the Store"; 103 SinkWarning() << "Unimplemented add of a 'Journal' item in the Store";
98 break; 104 break;
@@ -112,58 +118,74 @@ protected:
112 return syncStore().resolveRemoteId(ENTITY_TYPE_CALENDAR, resourceID(calendar)); 118 return syncStore().resolveRemoteId(ENTITY_TYPE_CALENDAR, resourceID(calendar));
113 } 119 }
114 120
115 KAsync::Job<QByteArray> replay(const Event &event, Sink::Operation operation, 121 template<typename Item>
116 const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE 122 KAsync::Job<QByteArray> replayItem(const Item &localItem, Sink::Operation operation,
123 const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties,
124 const QByteArray &entityType)
117 { 125 {
118 SinkLog() << "Replaying event"; 126 SinkLog() << "Replaying" << entityType;
119 127
120 KDAV2::DavItem item; 128 KDAV2::DavItem remoteItem;
121 129
122 switch (operation) { 130 switch (operation) {
123 case Sink::Operation_Creation: { 131 case Sink::Operation_Creation: {
124 auto rawIcal = event.getIcal(); 132 auto rawIcal = localItem.getIcal();
125 if(rawIcal == "") { 133 if (rawIcal == "") {
126 return KAsync::error<QByteArray>("No ICal in event for creation replay"); 134 return KAsync::error<QByteArray>("No ICal in item for creation replay");
127 } 135 }
128 136
129 auto collectionId = syncStore().resolveLocalId(ENTITY_TYPE_CALENDAR, event.getCalendar()); 137 auto collectionId = syncStore().resolveLocalId(ENTITY_TYPE_CALENDAR, localItem.getCalendar());
130 138
131 item.setData(rawIcal); 139 remoteItem.setData(rawIcal);
132 item.setContentType("text/calendar"); 140 remoteItem.setContentType("text/calendar");
133 item.setUrl(urlOf(collectionId, event.getUid())); 141 remoteItem.setUrl(urlOf(collectionId, localItem.getUid()));
134 142
135 SinkLog() << "Creating event:" << event.getSummary(); 143 SinkLog() << "Creating" << entityType << ":" << localItem.getSummary();
136 return createItem(item).then([item] { return resourceID(item); }); 144 return createItem(remoteItem).then([remoteItem] { return resourceID(remoteItem); });
137 } 145 }
138 case Sink::Operation_Removal: { 146 case Sink::Operation_Removal: {
139 // We only need the URL in the DAV item for removal 147 // We only need the URL in the DAV item for removal
140 item.setUrl(urlOf(oldRemoteId)); 148 remoteItem.setUrl(urlOf(oldRemoteId));
141 149
142 SinkLog() << "Removing event:" << oldRemoteId; 150 SinkLog() << "Removing" << entityType << ":" << oldRemoteId;
143 return removeItem(item).then([] { return QByteArray{}; }); 151 return removeItem(remoteItem).then([] { return QByteArray{}; });
144 } 152 }
145 case Sink::Operation_Modification: 153 case Sink::Operation_Modification:
146 auto rawIcal = event.getIcal(); 154 auto rawIcal = localItem.getIcal();
147 if(rawIcal == "") { 155 if (rawIcal == "") {
148 return KAsync::error<QByteArray>("No ICal in event for modification replay"); 156 return KAsync::error<QByteArray>("No ICal in item for modification replay");
149 } 157 }
150 158
151 item.setData(rawIcal); 159 remoteItem.setData(rawIcal);
152 item.setContentType("text/calendar"); 160 remoteItem.setContentType("text/calendar");
153 item.setUrl(urlOf(oldRemoteId)); 161 remoteItem.setUrl(urlOf(oldRemoteId));
154 162
155 SinkLog() << "Modifying event:" << event.getSummary(); 163 SinkLog() << "Modifying" << entityType << ":" << localItem.getSummary();
156 164
157 // It would be nice to check that the URL of the item hasn't 165 // It would be nice to check that the URL of the item hasn't
158 // changed and move he item if it did, but since the URL is 166 // changed and move he item if it did, but since the URL is
159 // pretty much arbitrary, whoe does that anyway? 167 // pretty much arbitrary, whoe does that anyway?
160 return modifyItem(item).then([oldRemoteId] { return oldRemoteId; }); 168 return modifyItem(remoteItem).then([oldRemoteId] { return oldRemoteId; });
161 } 169 }
162 } 170 }
163 171
172 KAsync::Job<QByteArray> replay(const Event &event, Sink::Operation operation,
173 const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
174 {
175 return replayItem(event, operation, oldRemoteId, changedProperties, ENTITY_TYPE_EVENT);
176 }
177
178 KAsync::Job<QByteArray> replay(const Todo &todo, Sink::Operation operation,
179 const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
180 {
181 return replayItem(todo, operation, oldRemoteId, changedProperties, ENTITY_TYPE_TODO);
182 }
183
164 KAsync::Job<QByteArray> replay(const Calendar &calendar, Sink::Operation operation, 184 KAsync::Job<QByteArray> replay(const Calendar &calendar, Sink::Operation operation,
165 const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE 185 const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
166 { 186 {
187 SinkLog() << "Replaying calendar";
188
167 switch (operation) { 189 switch (operation) {
168 case Sink::Operation_Creation: 190 case Sink::Operation_Creation:
169 SinkWarning() << "Unimplemented replay of calendar creation"; 191 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 @@
22using Sink::ApplicationDomain::Calendar; 22using Sink::ApplicationDomain::Calendar;
23using Sink::ApplicationDomain::DummyResource; 23using Sink::ApplicationDomain::DummyResource;
24using Sink::ApplicationDomain::Event; 24using Sink::ApplicationDomain::Event;
25using Sink::ApplicationDomain::Todo;
25using Sink::ApplicationDomain::SinkResource; 26using Sink::ApplicationDomain::SinkResource;
26 27
27class CalDavTest : public QObject 28class CalDavTest : public QObject
28{ 29{
29 Q_OBJECT 30 Q_OBJECT
30 31
31 // This test assumes a calendar MyCalendar with one event in it. 32 // This test assumes a calendar MyCalendar with one event and one todo in
33 // it.
32 34
33 const QString baseUrl = "http://localhost/dav/calendars/users/doe"; 35 const QString baseUrl = "http://localhost/dav/calendars/users/doe";
34 const QString username = "doe"; 36 const QString username = "doe";
@@ -47,6 +49,7 @@ class CalDavTest : public QObject
47 QByteArray mResourceInstanceIdentifier; 49 QByteArray mResourceInstanceIdentifier;
48 50
49 QString addedEventUid; 51 QString addedEventUid;
52 QString addedTodoUid;
50 53
51private slots: 54private slots:
52 55
@@ -74,6 +77,7 @@ private slots:
74 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); 77 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
75 // Check in the logs that it doesn't synchronize events again because same CTag 78 // Check in the logs that it doesn't synchronize events again because same CTag
76 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); 79 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
80 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier));
77 } 81 }
78 82
79 void testSyncCalEmpty() 83 void testSyncCalEmpty()
@@ -83,7 +87,11 @@ private slots:
83 87
84 auto eventJob = Sink::Store::fetchAll<Event>(Sink::Query().request<Event::Uid>()) 88 auto eventJob = Sink::Store::fetchAll<Event>(Sink::Query().request<Event::Uid>())
85 .then([](const QList<Event::Ptr> &events) { QCOMPARE(events.size(), 1); }); 89 .then([](const QList<Event::Ptr> &events) { QCOMPARE(events.size(), 1); });
90 auto todoJob = Sink::Store::fetchAll<Todo>(Sink::Query().request<Todo::Uid>())
91 .then([](const QList<Todo::Ptr> &todos) { QCOMPARE(todos.size(), 1); });
92
86 VERIFYEXEC(eventJob); 93 VERIFYEXEC(eventJob);
94 VERIFYEXEC(todoJob);
87 95
88 auto calendarJob = Sink::Store::fetchAll<Calendar>(Sink::Query().request<Calendar::Name>()) 96 auto calendarJob = Sink::Store::fetchAll<Calendar>(Sink::Query().request<Calendar::Name>())
89 .then([](const QList<Calendar::Ptr> &calendars) { 97 .then([](const QList<Calendar::Ptr> &calendars) {
@@ -136,6 +144,44 @@ private slots:
136 VERIFYEXEC(verifyEventJob); 144 VERIFYEXEC(verifyEventJob);
137 } 145 }
138 146
147 void testAddTodo()
148 {
149 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
150 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier));
151
152 auto job = Sink::Store::fetchOne<Calendar>({}).exec();
153 job.waitForFinished();
154 QVERIFY2(!job.errorCode(), "Fetching Calendar failed");
155 auto calendar = job.value();
156
157 auto todo = QSharedPointer<KCalCore::Todo>::create();
158 todo->setSummary("Hello");
159 todo->setDtStart(QDateTime::currentDateTime());
160 todo->setCreated(QDateTime::currentDateTime());
161 addedTodoUid = QUuid::createUuid().toString();
162 todo->setUid(addedTodoUid);
163
164 auto ical = KCalCore::ICalFormat().toICalString(todo);
165 Todo sinkTodo(mResourceInstanceIdentifier);
166 sinkTodo.setIcal(ical.toUtf8());
167 sinkTodo.setCalendar(calendar);
168
169 SinkLog() << "Adding todo";
170 VERIFYEXEC(Sink::Store::create(sinkTodo));
171 VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier));
172
173 auto verifyTodoCountJob =
174 Sink::Store::fetchAll<Todo>(Sink::Query().request<Todo::Uid>()).then([](const QList<Todo::Ptr> &todos) {
175 QCOMPARE(todos.size(), 2);
176 });
177 VERIFYEXEC(verifyTodoCountJob);
178
179 auto verifyTodoJob =
180 Sink::Store::fetchOne<Todo>(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid)))
181 .then([](const Todo &todo) { QCOMPARE(todo.getSummary(), "Hello"); });
182 VERIFYEXEC(verifyTodoJob);
183 }
184
139 void testModifyEvent() 185 void testModifyEvent()
140 { 186 {
141 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); 187 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
@@ -173,6 +219,43 @@ private slots:
173 VERIFYEXEC(verifyEventJob); 219 VERIFYEXEC(verifyEventJob);
174 } 220 }
175 221
222 void testModifyTodo()
223 {
224 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
225 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier));
226
227 auto job = Sink::Store::fetchOne<Todo>(
228 Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid)))
229 .exec();
230 job.waitForFinished();
231 QVERIFY2(!job.errorCode(), "Fetching Todo failed");
232 auto todo = job.value();
233
234 auto incidence = KCalCore::ICalFormat().readIncidence(todo.getIcal());
235 auto caltodo = incidence.dynamicCast<KCalCore::Todo>();
236 QVERIFY2(caltodo, "Cannot convert to KCalCore todo");
237
238 caltodo->setSummary("Hello World!");
239 auto dummy = QSharedPointer<KCalCore::Todo>(caltodo);
240 auto newical = KCalCore::ICalFormat().toICalString(dummy);
241
242 todo.setIcal(newical.toUtf8());
243
244 VERIFYEXEC(Sink::Store::modify(todo));
245
246 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
247 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier));
248
249 auto verifyTodoCountJob = Sink::Store::fetchAll<Todo>({}).then(
250 [](const QList<Todo::Ptr> &todos) { QCOMPARE(todos.size(), 2); });
251 VERIFYEXEC(verifyTodoCountJob);
252
253 auto verifyTodoJob =
254 Sink::Store::fetchOne<Todo>(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid)))
255 .then([](const Todo &todo) { QCOMPARE(todo.getSummary(), "Hello World!"); });
256 VERIFYEXEC(verifyTodoJob);
257 }
258
176 void testSneakyModifyEvent() 259 void testSneakyModifyEvent()
177 { 260 {
178 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); 261 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
@@ -180,7 +263,6 @@ private slots:
180 263
181 // Change the item without sink's knowledge 264 // Change the item without sink's knowledge
182 { 265 {
183 qWarning() << 1;
184 auto collection = ([this]() -> KDAV2::DavCollection { 266 auto collection = ([this]() -> KDAV2::DavCollection {
185 QUrl url(baseUrl); 267 QUrl url(baseUrl);
186 url.setUserName(username); 268 url.setUserName(username);
@@ -212,22 +294,17 @@ private slots:
212 return itemFetchJob.item(); 294 return itemFetchJob.item();
213 })(); 295 })();
214 296
215 qWarning() << 3;
216 auto incidence = KCalCore::ICalFormat().readIncidence(davitem.data()); 297 auto incidence = KCalCore::ICalFormat().readIncidence(davitem.data());
217 auto calevent = incidence.dynamicCast<KCalCore::Event>(); 298 auto calevent = incidence.dynamicCast<KCalCore::Event>();
218 QVERIFY2(calevent, "Cannot convert to KCalCore event"); 299 QVERIFY2(calevent, "Cannot convert to KCalCore event");
219 300
220 qWarning() << 4;
221 calevent->setSummary("Manual Hello World!"); 301 calevent->setSummary("Manual Hello World!");
222 auto newical = KCalCore::ICalFormat().toICalString(calevent); 302 auto newical = KCalCore::ICalFormat().toICalString(calevent);
223 303
224 qWarning() << 5;
225 davitem.setData(newical.toUtf8()); 304 davitem.setData(newical.toUtf8());
226 KDAV2::DavItemModifyJob itemModifyJob(davitem); 305 KDAV2::DavItemModifyJob itemModifyJob(davitem);
227 itemModifyJob.exec(); 306 itemModifyJob.exec();
228 QVERIFY2(itemModifyJob.error() == 0, "Cannot modify item"); 307 QVERIFY2(itemModifyJob.error() == 0, "Cannot modify item");
229
230 qWarning() << 6;
231 } 308 }
232 309
233 // Try to change the item with sink 310 // Try to change the item with sink
@@ -274,6 +351,26 @@ private slots:
274 [](const QList<Event::Ptr> &events) { QCOMPARE(events.size(), 1); }); 351 [](const QList<Event::Ptr> &events) { QCOMPARE(events.size(), 1); });
275 VERIFYEXEC(verifyEventCountJob); 352 VERIFYEXEC(verifyEventCountJob);
276 } 353 }
354
355 void testRemoveTodo()
356 {
357 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
358 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier));
359
360 auto job = Sink::Store::fetchOne<Todo>(
361 Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid)))
362 .exec();
363 job.waitForFinished();
364 QVERIFY2(!job.errorCode(), "Fetching Todo failed");
365 auto todo = job.value();
366
367 VERIFYEXEC(Sink::Store::remove(todo));
368 VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier));
369
370 auto verifyTodoCountJob = Sink::Store::fetchAll<Todo>({}).then(
371 [](const QList<Todo::Ptr> &todos) { QCOMPARE(todos.size(), 1); });
372 VERIFYEXEC(verifyTodoCountJob);
373 }
277}; 374};
278 375
279QTEST_MAIN(CalDavTest) 376QTEST_MAIN(CalDavTest)