diff options
author | Minijackson <minijackson@riseup.net> | 2018-05-04 12:43:33 +0200 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2018-05-04 12:43:33 +0200 |
commit | ee074c7b460789e8fefb0d127ba217529fb88e2c (patch) | |
tree | b32536cf633ba00893cfb84917469ef8e101a0ce | |
parent | 404d4619ccfc2ea014bdec1a26a9c88d1c314227 (diff) | |
download | sink-ee074c7b460789e8fefb0d127ba217529fb88e2c.tar.gz sink-ee074c7b460789e8fefb0d127ba217529fb88e2c.zip |
Add Todo support in caldav synchronizer
-rw-r--r-- | examples/caldavresource/caldavresource.cpp | 80 | ||||
-rw-r--r-- | 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: | |||
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 @@ | |||
22 | using Sink::ApplicationDomain::Calendar; | 22 | using Sink::ApplicationDomain::Calendar; |
23 | using Sink::ApplicationDomain::DummyResource; | 23 | using Sink::ApplicationDomain::DummyResource; |
24 | using Sink::ApplicationDomain::Event; | 24 | using Sink::ApplicationDomain::Event; |
25 | using Sink::ApplicationDomain::Todo; | ||
25 | using Sink::ApplicationDomain::SinkResource; | 26 | using Sink::ApplicationDomain::SinkResource; |
26 | 27 | ||
27 | class CalDavTest : public QObject | 28 | class 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 | ||
51 | private slots: | 54 | private 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 | ||
279 | QTEST_MAIN(CalDavTest) | 376 | QTEST_MAIN(CalDavTest) |