diff options
-rw-r--r-- | common/domain/typeimplementations.cpp | 7 | ||||
-rw-r--r-- | common/log.cpp | 16 | ||||
-rw-r--r-- | common/storage/entitystore.cpp | 2 | ||||
-rw-r--r-- | examples/caldavresource/CMakeLists.txt | 2 | ||||
-rw-r--r-- | examples/caldavresource/tests/caldavtest.cpp | 391 | ||||
-rw-r--r-- | examples/carddavresource/CMakeLists.txt | 2 | ||||
-rw-r--r-- | examples/carddavresource/tests/carddavtest.cpp | 75 | ||||
-rw-r--r-- | examples/mailtransportresource/mailtransport.cpp | 22 | ||||
-rw-r--r-- | examples/mailtransportresource/mailtransport.h | 3 | ||||
-rw-r--r-- | examples/mailtransportresource/mailtransportresource.cpp | 6 | ||||
-rw-r--r-- | examples/webdavcommon/CMakeLists.txt | 2 | ||||
-rw-r--r-- | examples/webdavcommon/webdav.cpp | 34 | ||||
-rw-r--r-- | examples/webdavcommon/webdav.h | 4 | ||||
-rw-r--r-- | tests/dbwriter.cpp | 12 | ||||
-rw-r--r-- | tests/pipelinetest.cpp | 6 |
15 files changed, 349 insertions, 235 deletions
diff --git a/common/domain/typeimplementations.cpp b/common/domain/typeimplementations.cpp index c74701d..6e14501 100644 --- a/common/domain/typeimplementations.cpp +++ b/common/domain/typeimplementations.cpp | |||
@@ -56,7 +56,8 @@ typedef IndexConfig<Folder, | |||
56 | > FolderIndexConfig; | 56 | > FolderIndexConfig; |
57 | 57 | ||
58 | typedef IndexConfig<Contact, | 58 | typedef IndexConfig<Contact, |
59 | ValueIndex<Contact::Uid> | 59 | ValueIndex<Contact::Uid>, |
60 | ValueIndex<Contact::Addressbook> | ||
60 | > ContactIndexConfig; | 61 | > ContactIndexConfig; |
61 | 62 | ||
62 | typedef IndexConfig<Addressbook, | 63 | typedef IndexConfig<Addressbook, |
@@ -65,12 +66,14 @@ typedef IndexConfig<Addressbook, | |||
65 | 66 | ||
66 | typedef IndexConfig<Event, | 67 | typedef IndexConfig<Event, |
67 | ValueIndex<Event::Uid>, | 68 | ValueIndex<Event::Uid>, |
69 | ValueIndex<Event::Calendar>, | ||
68 | SortedIndex<Event::StartTime>, | 70 | SortedIndex<Event::StartTime>, |
69 | SampledPeriodIndex<Event::StartTime, Event::EndTime> | 71 | SampledPeriodIndex<Event::StartTime, Event::EndTime> |
70 | > EventIndexConfig; | 72 | > EventIndexConfig; |
71 | 73 | ||
72 | typedef IndexConfig<Todo, | 74 | typedef IndexConfig<Todo, |
73 | ValueIndex<Todo::Uid> | 75 | ValueIndex<Todo::Uid>, |
76 | ValueIndex<Todo::Calendar> | ||
74 | > TodoIndexConfig; | 77 | > TodoIndexConfig; |
75 | 78 | ||
76 | typedef IndexConfig<Calendar, | 79 | typedef IndexConfig<Calendar, |
diff --git a/common/log.cpp b/common/log.cpp index 5156a23..e5ed29b 100644 --- a/common/log.cpp +++ b/common/log.cpp | |||
@@ -253,14 +253,22 @@ class DebugAreaCollector { | |||
253 | public: | 253 | public: |
254 | DebugAreaCollector() | 254 | DebugAreaCollector() |
255 | { | 255 | { |
256 | QMutexLocker locker(&mutex); | 256 | //This call can potentially print a log message (if we fail to remove the qsettings lockfile), which would result in a deadlock if we locked over all of it. |
257 | mDebugAreas = debugAreasConfig()->value("areas").value<QString>().split(';').toSet(); | 257 | const auto areas = debugAreasConfig()->value("areas").value<QString>().split(';').toSet(); |
258 | { | ||
259 | QMutexLocker locker(&mutex); | ||
260 | mDebugAreas = areas; | ||
261 | } | ||
258 | } | 262 | } |
259 | 263 | ||
260 | ~DebugAreaCollector() | 264 | ~DebugAreaCollector() |
261 | { | 265 | { |
262 | QMutexLocker locker(&mutex); | 266 | //This call can potentially print a log message (if we fail to remove the qsettings lockfile), which would result in a deadlock if we locked over all of it. |
263 | mDebugAreas += debugAreasConfig()->value("areas").value<QString>().split(';').toSet(); | 267 | const auto areas = debugAreasConfig()->value("areas").value<QString>().split(';').toSet(); |
268 | { | ||
269 | QMutexLocker locker(&mutex); | ||
270 | mDebugAreas += areas; | ||
271 | } | ||
264 | debugAreasConfig()->setValue("areas", QVariant::fromValue(mDebugAreas.toList().join(';'))); | 272 | debugAreasConfig()->setValue("areas", QVariant::fromValue(mDebugAreas.toList().join(';'))); |
265 | } | 273 | } |
266 | 274 | ||
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index c11f49c..daabf1f 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp | |||
@@ -458,7 +458,7 @@ QVector<Identifier> EntityStore::fullScan(const QByteArray &type) | |||
458 | const auto identifier = Sink::Storage::Identifier::fromDisplayByteArray(uid); | 458 | const auto identifier = Sink::Storage::Identifier::fromDisplayByteArray(uid); |
459 | if (keys.contains(identifier)) { | 459 | if (keys.contains(identifier)) { |
460 | //Not something that should persist if the replay works, so we keep a message for now. | 460 | //Not something that should persist if the replay works, so we keep a message for now. |
461 | SinkTraceCtx(d->logCtx) << "Multiple revisions for key: " << key; | 461 | SinkTraceCtx(d->logCtx) << "Multiple revisions for uid: " << Sink::Storage::Key::fromInternalByteArray(key) << ". This is normal if changereplay has not completed yet."; |
462 | } | 462 | } |
463 | keys << identifier; | 463 | keys << identifier; |
464 | return true; | 464 | return true; |
diff --git a/examples/caldavresource/CMakeLists.txt b/examples/caldavresource/CMakeLists.txt index d6e8408..00d1eb8 100644 --- a/examples/caldavresource/CMakeLists.txt +++ b/examples/caldavresource/CMakeLists.txt | |||
@@ -2,7 +2,7 @@ project(sink_resource_caldav) | |||
2 | 2 | ||
3 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | 3 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
4 | 4 | ||
5 | find_package(KPimKDAV2 REQUIRED) | 5 | find_package(KPimKDAV2 REQUIRED 0.2.0) |
6 | find_package(KF5CalendarCore REQUIRED) | 6 | find_package(KF5CalendarCore REQUIRED) |
7 | 7 | ||
8 | add_library(${PROJECT_NAME} SHARED caldavresource.cpp) | 8 | add_library(${PROJECT_NAME} SHARED caldavresource.cpp) |
diff --git a/examples/caldavresource/tests/caldavtest.cpp b/examples/caldavresource/tests/caldavtest.cpp index daa890d..d62616f 100644 --- a/examples/caldavresource/tests/caldavtest.cpp +++ b/examples/caldavresource/tests/caldavtest.cpp | |||
@@ -1,8 +1,10 @@ | |||
1 | #include <QtTest> | 1 | #include <QtTest> |
2 | 2 | ||
3 | #include <KDAV2/DavCollectionsFetchJob> | 3 | #include <KDAV2/DavCollectionsFetchJob> |
4 | #include <KDAV2/DavCollectionCreateJob> | ||
4 | #include <KDAV2/DavItemFetchJob> | 5 | #include <KDAV2/DavItemFetchJob> |
5 | #include <KDAV2/DavItemModifyJob> | 6 | #include <KDAV2/DavItemModifyJob> |
7 | #include <KDAV2/DavItemCreateJob> | ||
6 | #include <KDAV2/DavItemsListJob> | 8 | #include <KDAV2/DavItemsListJob> |
7 | #include <KDAV2/EtagCache> | 9 | #include <KDAV2/EtagCache> |
8 | 10 | ||
@@ -41,20 +43,82 @@ class CalDavTest : public QObject | |||
41 | resource.setProperty("server", baseUrl); | 43 | resource.setProperty("server", baseUrl); |
42 | resource.setProperty("username", username); | 44 | resource.setProperty("username", username); |
43 | Sink::SecretStore::instance().insert(resource.identifier(), password); | 45 | Sink::SecretStore::instance().insert(resource.identifier(), password); |
44 | resource.setProperty("testmode", true); | ||
45 | return resource; | 46 | return resource; |
46 | } | 47 | } |
47 | 48 | ||
48 | QByteArray mResourceInstanceIdentifier; | 49 | QByteArray mResourceInstanceIdentifier; |
49 | 50 | ||
50 | QString addedEventUid; | 51 | QByteArray createEvent(const QString &subject, const QString &collectionName) |
51 | QString addedTodoUid; | 52 | { |
53 | QUrl mainUrl{"http://localhost/dav/calendars/user/doe"}; | ||
54 | mainUrl.setUserName(QStringLiteral("doe")); | ||
55 | mainUrl.setPassword(QStringLiteral("doe")); | ||
56 | |||
57 | KDAV2::DavUrl davUrl(mainUrl, KDAV2::CalDav); | ||
58 | |||
59 | auto *job = new KDAV2::DavCollectionsFetchJob(davUrl); | ||
60 | job->exec(); | ||
61 | |||
62 | const auto collectionUrl = [&] { | ||
63 | for (const auto &col : job->collections()) { | ||
64 | // qWarning() << "Looking for " << collectionName << col.displayName(); | ||
65 | if (col.displayName() == collectionName) { | ||
66 | return col.url().url(); | ||
67 | } | ||
68 | } | ||
69 | return QUrl{}; | ||
70 | }(); | ||
71 | |||
72 | QUrl url{collectionUrl.toString() + subject + ".ical"}; | ||
73 | url.setUserInfo(mainUrl.userInfo()); | ||
74 | |||
75 | KDAV2::DavUrl testItemUrl(url, KDAV2::CardDav); | ||
76 | |||
77 | auto event = QSharedPointer<KCalCore::Event>::create(); | ||
78 | event->setSummary(subject); | ||
79 | event->setDtStart(QDateTime::currentDateTime()); | ||
80 | event->setDtEnd(QDateTime::currentDateTime().addSecs(3600)); | ||
81 | event->setCreated(QDateTime::currentDateTime()); | ||
82 | event->setUid(subject); | ||
83 | |||
84 | auto data = KCalCore::ICalFormat().toICalString(event).toUtf8(); | ||
85 | |||
86 | KDAV2::DavItem item(testItemUrl, QStringLiteral("text/calendar"), data, QString()); | ||
87 | auto createJob = new KDAV2::DavItemCreateJob(item); | ||
88 | createJob->exec(); | ||
89 | if (createJob->error()) { | ||
90 | qWarning() << createJob->errorString(); | ||
91 | } | ||
92 | return event->uid().toUtf8(); | ||
93 | } | ||
94 | |||
95 | void createCollection(const QString &name) | ||
96 | { | ||
97 | QUrl mainUrl(QStringLiteral("http://localhost/dav/calendars/user/doe/") + name); | ||
98 | mainUrl.setUserName(QStringLiteral("doe")); | ||
99 | mainUrl.setPassword(QStringLiteral("doe")); | ||
100 | |||
101 | KDAV2::DavUrl davUrl(mainUrl, KDAV2::CalDav); | ||
102 | KDAV2::DavCollection collection{davUrl, name, KDAV2::DavCollection::Events}; | ||
103 | |||
104 | auto createJob = new KDAV2::DavCollectionCreateJob(collection); | ||
105 | createJob->exec(); | ||
106 | if (createJob->error()) { | ||
107 | qWarning() << createJob->errorString(); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | void resetTestEnvironment() | ||
112 | { | ||
113 | system("resetmailbox.sh"); | ||
114 | } | ||
52 | 115 | ||
53 | private slots: | 116 | private slots: |
54 | 117 | ||
55 | void initTestCase() | 118 | void initTestCase() |
56 | { | 119 | { |
57 | Sink::Test::initTest(); | 120 | Sink::Test::initTest(); |
121 | resetTestEnvironment(); | ||
58 | auto resource = createResource(); | 122 | auto resource = createResource(); |
59 | QVERIFY(!resource.identifier().isEmpty()); | 123 | QVERIFY(!resource.identifier().isEmpty()); |
60 | VERIFYEXEC(Sink::Store::create(resource)); | 124 | VERIFYEXEC(Sink::Store::create(resource)); |
@@ -76,42 +140,73 @@ private slots: | |||
76 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | 140 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); |
77 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 141 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
78 | 142 | ||
79 | auto eventJob = Sink::Store::fetchAll<Event>(Sink::Query().request<Event::Uid>()) | 143 | QCOMPARE(Sink::Store::read<Event>({}).size(), 0); |
80 | .then([](const QList<Event::Ptr> &events) { QCOMPARE(events.size(), 0); }); | 144 | QCOMPARE(Sink::Store::read<Todo>({}).size(), 0); |
81 | auto todoJob = Sink::Store::fetchAll<Todo>(Sink::Query().request<Todo::Uid>()) | 145 | |
82 | .then([](const QList<Todo::Ptr> &todos) { QCOMPARE(todos.size(), 0); }); | 146 | const auto calendars = Sink::Store::read<Calendar>(Sink::Query().request<Calendar::Name>()); |
147 | QCOMPARE(calendars.size(), 1); | ||
148 | QCOMPARE(calendars.first().getName(), {"personal"}); | ||
149 | } | ||
150 | |||
151 | void testSyncCalendars() | ||
152 | { | ||
153 | createCollection("calendar2"); | ||
154 | |||
155 | Sink::SyncScope scope; | ||
156 | scope.setType<Calendar>(); | ||
157 | scope.resourceFilter(mResourceInstanceIdentifier); | ||
158 | |||
159 | VERIFYEXEC(Sink::Store::synchronize(scope)); | ||
160 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
161 | const auto calendars = Sink::Store::read<Calendar>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); | ||
162 | QCOMPARE(calendars.size(), 2); | ||
163 | } | ||
83 | 164 | ||
84 | VERIFYEXEC(eventJob); | 165 | void testSyncEvents() |
85 | VERIFYEXEC(todoJob); | 166 | { |
167 | createEvent("event1", "personal"); | ||
168 | createEvent("event2", "personal"); | ||
169 | createEvent("event3", "calendar2"); | ||
170 | Sink::SyncScope scope; | ||
171 | scope.setType<Event>(); | ||
172 | scope.resourceFilter(mResourceInstanceIdentifier); | ||
173 | |||
174 | VERIFYEXEC(Sink::Store::synchronize(scope)); | ||
175 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
176 | const auto events = Sink::Store::read<Event>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); | ||
177 | QCOMPARE(events.size(), 3); | ||
86 | 178 | ||
87 | auto calendarJob = Sink::Store::fetchAll<Calendar>(Sink::Query().request<Calendar::Name>()) | 179 | //Ensure a resync works |
88 | .then([](const QList<Calendar::Ptr> &calendars) { | 180 | { |
89 | QCOMPARE(calendars.size(), 1); | 181 | VERIFYEXEC(Sink::Store::synchronize(scope)); |
90 | for (const auto &calendar : calendars) { | 182 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
91 | QVERIFY(calendar->getName() == "personal"); | 183 | const auto events = Sink::Store::read<Event>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); |
92 | } | 184 | QCOMPARE(events.size(), 3); |
93 | }); | 185 | } |
94 | VERIFYEXEC(calendarJob); | ||
95 | 186 | ||
96 | SinkLog() << "Finished"; | 187 | //Ensure a resync after another creation works |
188 | createEvent("event4", "calendar2"); | ||
189 | { | ||
190 | VERIFYEXEC(Sink::Store::synchronize(scope)); | ||
191 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
192 | const auto events = Sink::Store::read<Event>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); | ||
193 | QCOMPARE(events.size(), 4); | ||
194 | } | ||
97 | } | 195 | } |
98 | 196 | ||
99 | void testAddEvent() | 197 | void testCreateModifyDeleteEvent() |
100 | { | 198 | { |
101 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | 199 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); |
102 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 200 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
103 | 201 | ||
104 | auto future = Sink::Store::fetchOne<Calendar>({}).exec(); | 202 | auto calendar = Sink::Store::readOne<Calendar>(Sink::Query{}.filter<Calendar::Name>("personal")); |
105 | future.waitForFinished(); | ||
106 | QVERIFY2(!future.errorCode(), "Fetching Calendar failed"); | ||
107 | auto calendar = future.value(); | ||
108 | 203 | ||
109 | auto event = QSharedPointer<KCalCore::Event>::create(); | 204 | auto event = QSharedPointer<KCalCore::Event>::create(); |
110 | event->setSummary("Hello"); | 205 | event->setSummary("Hello"); |
111 | event->setDtStart(QDateTime::currentDateTime()); | 206 | event->setDtStart(QDateTime::currentDateTime()); |
112 | event->setDtEnd(QDateTime::currentDateTime().addSecs(3600)); | 207 | event->setDtEnd(QDateTime::currentDateTime().addSecs(3600)); |
113 | event->setCreated(QDateTime::currentDateTime()); | 208 | event->setCreated(QDateTime::currentDateTime()); |
114 | addedEventUid = QUuid::createUuid().toString(); | 209 | auto addedEventUid = QUuid::createUuid().toString(); |
115 | event->setUid(addedEventUid); | 210 | event->setUid(addedEventUid); |
116 | 211 | ||
117 | auto ical = KCalCore::ICalFormat().toICalString(event); | 212 | auto ical = KCalCore::ICalFormat().toICalString(event); |
@@ -119,37 +214,60 @@ private slots: | |||
119 | sinkEvent.setIcal(ical.toUtf8()); | 214 | sinkEvent.setIcal(ical.toUtf8()); |
120 | sinkEvent.setCalendar(calendar); | 215 | sinkEvent.setCalendar(calendar); |
121 | 216 | ||
122 | SinkLog() << "Adding event"; | ||
123 | VERIFYEXEC(Sink::Store::create(sinkEvent)); | 217 | VERIFYEXEC(Sink::Store::create(sinkEvent)); |
124 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); | 218 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); |
125 | 219 | ||
126 | auto verifyEventCountJob = | 220 | auto events = Sink::Store::read<Event>(Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))); |
127 | Sink::Store::fetchAll<Event>(Sink::Query().request<Event::Uid>()).then([](const QList<Event::Ptr> &events) { | 221 | QCOMPARE(events.size(), 1); |
128 | QCOMPARE(events.size(), 1); | 222 | QCOMPARE(events.first().getSummary(), {"Hello"}); |
129 | }); | 223 | |
130 | VERIFYEXEC(verifyEventCountJob); | 224 | //Modify |
225 | { | ||
226 | auto event = events.first(); | ||
227 | auto incidence = KCalCore::ICalFormat().readIncidence(event.getIcal()); | ||
228 | auto calevent = incidence.dynamicCast<KCalCore::Event>(); | ||
229 | QVERIFY2(calevent, "Cannot convert to KCalCore event"); | ||
230 | |||
231 | calevent->setSummary("Hello World!"); | ||
232 | auto dummy = QSharedPointer<KCalCore::Event>(calevent); | ||
233 | auto newical = KCalCore::ICalFormat().toICalString(dummy); | ||
234 | |||
235 | event.setIcal(newical.toUtf8()); | ||
236 | |||
237 | VERIFYEXEC(Sink::Store::modify(event)); | ||
238 | |||
239 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | ||
240 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
131 | 241 | ||
132 | auto verifyEventJob = | 242 | auto events = Sink::Store::read<Event>(Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))); |
133 | Sink::Store::fetchOne<Event>(Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))) | 243 | QCOMPARE(events.size(), 1); |
134 | .then([](const Event &event) { QCOMPARE(event.getSummary(), {"Hello"}); }); | 244 | QCOMPARE(events.first().getSummary(), {"Hello World!"}); |
135 | VERIFYEXEC(verifyEventJob); | 245 | } |
246 | //Delete | ||
247 | { | ||
248 | auto event = events.first(); | ||
249 | |||
250 | VERIFYEXEC(Sink::Store::remove(event)); | ||
251 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
252 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); | ||
253 | |||
254 | auto events = Sink::Store::read<Event>(Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))); | ||
255 | QCOMPARE(events.size(), 0); | ||
256 | } | ||
136 | } | 257 | } |
137 | 258 | ||
138 | void testAddTodo() | 259 | void testCreateModifyDeleteTodo() |
139 | { | 260 | { |
140 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | 261 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); |
141 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 262 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
142 | 263 | ||
143 | auto job = Sink::Store::fetchOne<Calendar>({}).exec(); | 264 | auto calendar = Sink::Store::readOne<Calendar>(Sink::Query{}.filter<Calendar::Name>("personal")); |
144 | job.waitForFinished(); | ||
145 | QVERIFY2(!job.errorCode(), "Fetching Calendar failed"); | ||
146 | auto calendar = job.value(); | ||
147 | 265 | ||
148 | auto todo = QSharedPointer<KCalCore::Todo>::create(); | 266 | auto todo = QSharedPointer<KCalCore::Todo>::create(); |
149 | todo->setSummary("Hello"); | 267 | todo->setSummary("Hello"); |
150 | todo->setDtStart(QDateTime::currentDateTime()); | 268 | todo->setDtStart(QDateTime::currentDateTime()); |
151 | todo->setCreated(QDateTime::currentDateTime()); | 269 | todo->setCreated(QDateTime::currentDateTime()); |
152 | addedTodoUid = QUuid::createUuid().toString(); | 270 | auto addedTodoUid = QUuid::createUuid().toString(); |
153 | todo->setUid(addedTodoUid); | 271 | todo->setUid(addedTodoUid); |
154 | 272 | ||
155 | auto ical = KCalCore::ICalFormat().toICalString(todo); | 273 | auto ical = KCalCore::ICalFormat().toICalString(todo); |
@@ -157,104 +275,74 @@ private slots: | |||
157 | sinkTodo.setIcal(ical.toUtf8()); | 275 | sinkTodo.setIcal(ical.toUtf8()); |
158 | sinkTodo.setCalendar(calendar); | 276 | sinkTodo.setCalendar(calendar); |
159 | 277 | ||
160 | SinkLog() << "Adding todo"; | ||
161 | VERIFYEXEC(Sink::Store::create(sinkTodo)); | 278 | VERIFYEXEC(Sink::Store::create(sinkTodo)); |
162 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); | 279 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); |
163 | 280 | ||
164 | auto verifyTodoCountJob = | 281 | auto todos = Sink::Store::read<Todo>(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))); |
165 | Sink::Store::fetchAll<Todo>(Sink::Query().request<Todo::Uid>()).then([](const QList<Todo::Ptr> &todos) { | 282 | QCOMPARE(todos.size(), 1); |
166 | QCOMPARE(todos.size(), 1); | 283 | QCOMPARE(todos.first().getSummary(), {"Hello"}); |
167 | }); | ||
168 | VERIFYEXEC(verifyTodoCountJob); | ||
169 | 284 | ||
170 | auto verifyTodoJob = | 285 | //Modify |
171 | Sink::Store::fetchOne<Todo>(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))) | 286 | { |
172 | .then([](const Todo &todo) { QCOMPARE(todo.getSummary(), {"Hello"}); }); | 287 | auto todo = todos.first(); |
173 | VERIFYEXEC(verifyTodoJob); | 288 | auto incidence = KCalCore::ICalFormat().readIncidence(todo.getIcal()); |
174 | } | 289 | auto caltodo = incidence.dynamicCast<KCalCore::Todo>(); |
175 | 290 | QVERIFY2(caltodo, "Cannot convert to KCalCore todo"); | |
176 | void testModifyEvent() | ||
177 | { | ||
178 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | ||
179 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
180 | |||
181 | auto job = Sink::Store::fetchOne<Event>( | ||
182 | Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))) | ||
183 | .exec(); | ||
184 | job.waitForFinished(); | ||
185 | QVERIFY2(!job.errorCode(), "Fetching Event failed"); | ||
186 | auto event = job.value(); | ||
187 | 291 | ||
188 | auto incidence = KCalCore::ICalFormat().readIncidence(event.getIcal()); | 292 | caltodo->setSummary("Hello World!"); |
189 | auto calevent = incidence.dynamicCast<KCalCore::Event>(); | 293 | auto dummy = QSharedPointer<KCalCore::Todo>(caltodo); |
190 | QVERIFY2(calevent, "Cannot convert to KCalCore event"); | 294 | auto newical = KCalCore::ICalFormat().toICalString(dummy); |
191 | 295 | ||
192 | calevent->setSummary("Hello World!"); | 296 | todo.setIcal(newical.toUtf8()); |
193 | auto dummy = QSharedPointer<KCalCore::Event>(calevent); | ||
194 | auto newical = KCalCore::ICalFormat().toICalString(dummy); | ||
195 | 297 | ||
196 | event.setIcal(newical.toUtf8()); | 298 | VERIFYEXEC(Sink::Store::modify(todo)); |
197 | 299 | ||
198 | VERIFYEXEC(Sink::Store::modify(event)); | 300 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); |
301 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
199 | 302 | ||
200 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | 303 | auto todos = Sink::Store::read<Todo>(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))); |
201 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 304 | QCOMPARE(todos.size(), 1); |
305 | QCOMPARE(todos.first().getSummary(), {"Hello World!"}); | ||
306 | } | ||
307 | //Delete | ||
308 | { | ||
309 | auto todo = todos.first(); | ||
202 | 310 | ||
203 | auto verifyEventCountJob = Sink::Store::fetchAll<Event>({}).then( | 311 | VERIFYEXEC(Sink::Store::remove(todo)); |
204 | [](const QList<Event::Ptr> &events) { QCOMPARE(events.size(), 1); }); | 312 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
205 | VERIFYEXEC(verifyEventCountJob); | 313 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); |
206 | 314 | ||
207 | auto verifyEventJob = | 315 | auto todos = Sink::Store::read<Todo>(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))); |
208 | Sink::Store::fetchOne<Event>(Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))) | 316 | QCOMPARE(todos.size(), 0); |
209 | .then([](const Event &event) { QCOMPARE(event.getSummary(), {"Hello World!"}); }); | 317 | } |
210 | VERIFYEXEC(verifyEventJob); | ||
211 | } | 318 | } |
212 | 319 | ||
213 | void testModifyTodo() | 320 | void testModificationConflict() |
214 | { | 321 | { |
215 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | 322 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); |
216 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 323 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
217 | 324 | ||
218 | auto job = Sink::Store::fetchOne<Todo>( | 325 | auto calendar = Sink::Store::readOne<Calendar>(Sink::Query{}.filter<Calendar::Name>("personal")); |
219 | Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))) | ||
220 | .exec(); | ||
221 | job.waitForFinished(); | ||
222 | QVERIFY2(!job.errorCode(), "Fetching Todo failed"); | ||
223 | auto todo = job.value(); | ||
224 | |||
225 | auto incidence = KCalCore::ICalFormat().readIncidence(todo.getIcal()); | ||
226 | auto caltodo = incidence.dynamicCast<KCalCore::Todo>(); | ||
227 | QVERIFY2(caltodo, "Cannot convert to KCalCore todo"); | ||
228 | |||
229 | caltodo->setSummary("Hello World!"); | ||
230 | auto dummy = QSharedPointer<KCalCore::Todo>(caltodo); | ||
231 | auto newical = KCalCore::ICalFormat().toICalString(dummy); | ||
232 | |||
233 | todo.setIcal(newical.toUtf8()); | ||
234 | |||
235 | VERIFYEXEC(Sink::Store::modify(todo)); | ||
236 | 326 | ||
237 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | 327 | auto event = QSharedPointer<KCalCore::Event>::create(); |
238 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 328 | event->setSummary("Hello"); |
239 | 329 | event->setDtStart(QDateTime::currentDateTime()); | |
240 | auto verifyTodoCountJob = Sink::Store::fetchAll<Todo>({}).then( | 330 | event->setDtEnd(QDateTime::currentDateTime().addSecs(3600)); |
241 | [](const QList<Todo::Ptr> &todos) { QCOMPARE(todos.size(), 1); }); | 331 | event->setCreated(QDateTime::currentDateTime()); |
242 | VERIFYEXEC(verifyTodoCountJob); | 332 | auto addedEventUid = QUuid::createUuid().toString(); |
333 | event->setUid(addedEventUid); | ||
243 | 334 | ||
244 | auto verifyTodoJob = | 335 | auto ical = KCalCore::ICalFormat().toICalString(event); |
245 | Sink::Store::fetchOne<Todo>(Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))) | 336 | Event sinkEvent(mResourceInstanceIdentifier); |
246 | .then([](const Todo &todo) { QCOMPARE(todo.getSummary(), {"Hello World!"}); }); | 337 | sinkEvent.setIcal(ical.toUtf8()); |
247 | VERIFYEXEC(verifyTodoJob); | 338 | sinkEvent.setCalendar(calendar); |
248 | } | ||
249 | 339 | ||
250 | void testSneakyModifyEvent() | 340 | VERIFYEXEC(Sink::Store::create(sinkEvent)); |
251 | { | 341 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); |
252 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | ||
253 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
254 | 342 | ||
255 | // Change the item without sink's knowledge | 343 | // Change the item without sink's knowledge |
256 | { | 344 | { |
257 | auto collection = ([this]() -> KDAV2::DavCollection { | 345 | auto collection = [&]() -> KDAV2::DavCollection { |
258 | QUrl url(baseUrl); | 346 | QUrl url(baseUrl); |
259 | url.setUserName(username); | 347 | url.setUserName(username); |
260 | url.setPassword(password); | 348 | url.setPassword(password); |
@@ -262,8 +350,13 @@ private slots: | |||
262 | auto collectionsJob = new KDAV2::DavCollectionsFetchJob(davurl); | 350 | auto collectionsJob = new KDAV2::DavCollectionsFetchJob(davurl); |
263 | collectionsJob->exec(); | 351 | collectionsJob->exec(); |
264 | Q_ASSERT(collectionsJob->error() == 0); | 352 | Q_ASSERT(collectionsJob->error() == 0); |
265 | return collectionsJob->collections()[0]; | 353 | for (const auto &col : collectionsJob->collections()) { |
266 | })(); | 354 | if (col.displayName() == "personal") { |
355 | return col; | ||
356 | } | ||
357 | } | ||
358 | return {}; | ||
359 | }(); | ||
267 | 360 | ||
268 | auto itemList = ([&collection]() -> KDAV2::DavItem::List { | 361 | auto itemList = ([&collection]() -> KDAV2::DavItem::List { |
269 | auto cache = std::make_shared<KDAV2::EtagCache>(); | 362 | auto cache = std::make_shared<KDAV2::EtagCache>(); |
@@ -273,12 +366,12 @@ private slots: | |||
273 | return itemsListJob->items(); | 366 | return itemsListJob->items(); |
274 | })(); | 367 | })(); |
275 | auto hollowDavItemIt = | 368 | auto hollowDavItemIt = |
276 | std::find_if(itemList.begin(), itemList.end(), [this](const KDAV2::DavItem &item) { | 369 | std::find_if(itemList.begin(), itemList.end(), [&](const KDAV2::DavItem &item) { |
277 | return item.url().url().path().endsWith(addedEventUid); | 370 | return item.url().url().path().endsWith(addedEventUid); |
278 | }); | 371 | }); |
279 | QVERIFY(hollowDavItemIt != itemList.end()); | 372 | QVERIFY(hollowDavItemIt != itemList.end()); |
280 | 373 | ||
281 | auto davitem = ([this, &collection, &hollowDavItemIt]() -> KDAV2::DavItem { | 374 | auto davitem = ([&]() -> KDAV2::DavItem { |
282 | QString itemUrl = collection.url().url().toEncoded() + addedEventUid; | 375 | QString itemUrl = collection.url().url().toEncoded() + addedEventUid; |
283 | auto itemFetchJob = new KDAV2::DavItemFetchJob (*hollowDavItemIt); | 376 | auto itemFetchJob = new KDAV2::DavItemFetchJob (*hollowDavItemIt); |
284 | itemFetchJob->exec(); | 377 | itemFetchJob->exec(); |
@@ -299,70 +392,22 @@ private slots: | |||
299 | QVERIFY2(itemModifyJob->error() == 0, "Cannot modify item"); | 392 | QVERIFY2(itemModifyJob->error() == 0, "Cannot modify item"); |
300 | } | 393 | } |
301 | 394 | ||
302 | // Try to change the item with sink | 395 | //Change the item with sink as well |
303 | { | 396 | { |
304 | auto job = Sink::Store::fetchOne<Event>( | 397 | auto event = Sink::Store::readOne<Event>(Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))); |
305 | Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))) | 398 | auto calevent = KCalCore::ICalFormat().readIncidence(event.getIcal()).dynamicCast<KCalCore::Event>(); |
306 | .exec(); | 399 | QVERIFY(calevent); |
307 | job.waitForFinished(); | ||
308 | QVERIFY2(!job.errorCode(), "Fetching Event failed"); | ||
309 | auto event = job.value(); | ||
310 | |||
311 | auto incidence = KCalCore::ICalFormat().readIncidence(event.getIcal()); | ||
312 | auto calevent = incidence.dynamicCast<KCalCore::Event>(); | ||
313 | QVERIFY2(calevent, "Cannot convert to KCalCore event"); | ||
314 | 400 | ||
315 | calevent->setSummary("Sink Hello World!"); | 401 | calevent->setSummary("Sink Hello World!"); |
316 | auto dummy = QSharedPointer<KCalCore::Event>(calevent); | 402 | event.setIcal(KCalCore::ICalFormat().toICalString(calevent).toUtf8()); |
317 | auto newical = KCalCore::ICalFormat().toICalString(dummy); | ||
318 | 403 | ||
319 | event.setIcal(newical.toUtf8()); | 404 | // TODO: this produced a conflict, but we're not dealing with it in any way |
320 | |||
321 | // TODO: make that fail | ||
322 | VERIFYEXEC(Sink::Store::modify(event)); | 405 | VERIFYEXEC(Sink::Store::modify(event)); |
323 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); | 406 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); |
324 | } | 407 | } |
325 | } | 408 | } |
326 | 409 | ||
327 | void testRemoveEvent() | ||
328 | { | ||
329 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | ||
330 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
331 | |||
332 | auto job = Sink::Store::fetchOne<Event>( | ||
333 | Sink::Query().filter("uid", Sink::Query::Comparator(addedEventUid))) | ||
334 | .exec(); | ||
335 | job.waitForFinished(); | ||
336 | QVERIFY2(!job.errorCode(), "Fetching Event failed"); | ||
337 | auto event = job.value(); | ||
338 | |||
339 | VERIFYEXEC(Sink::Store::remove(event)); | ||
340 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); | ||
341 | |||
342 | auto verifyEventCountJob = Sink::Store::fetchAll<Event>({}).then( | ||
343 | [](const QList<Event::Ptr> &events) { QCOMPARE(events.size(), 0); }); | ||
344 | VERIFYEXEC(verifyEventCountJob); | ||
345 | } | ||
346 | |||
347 | void testRemoveTodo() | ||
348 | { | ||
349 | VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier))); | ||
350 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
351 | |||
352 | auto job = Sink::Store::fetchOne<Todo>( | ||
353 | Sink::Query().filter("uid", Sink::Query::Comparator(addedTodoUid))) | ||
354 | .exec(); | ||
355 | job.waitForFinished(); | ||
356 | QVERIFY2(!job.errorCode(), "Fetching Todo failed"); | ||
357 | auto todo = job.value(); | ||
358 | 410 | ||
359 | VERIFYEXEC(Sink::Store::remove(todo)); | ||
360 | VERIFYEXEC(Sink::ResourceControl::flushReplayQueue(mResourceInstanceIdentifier)); | ||
361 | |||
362 | auto verifyTodoCountJob = Sink::Store::fetchAll<Todo>({}).then( | ||
363 | [](const QList<Todo::Ptr> &todos) { QCOMPARE(todos.size(), 0); }); | ||
364 | VERIFYEXEC(verifyTodoCountJob); | ||
365 | } | ||
366 | }; | 411 | }; |
367 | 412 | ||
368 | QTEST_MAIN(CalDavTest) | 413 | QTEST_MAIN(CalDavTest) |
diff --git a/examples/carddavresource/CMakeLists.txt b/examples/carddavresource/CMakeLists.txt index 0632804..97208b1 100644 --- a/examples/carddavresource/CMakeLists.txt +++ b/examples/carddavresource/CMakeLists.txt | |||
@@ -2,7 +2,7 @@ project(sink_resource_carddav) | |||
2 | 2 | ||
3 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | 3 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
4 | 4 | ||
5 | find_package(KPimKDAV2 REQUIRED) | 5 | find_package(KPimKDAV2 REQUIRED 0.2.0) |
6 | 6 | ||
7 | add_library(${PROJECT_NAME} SHARED carddavresource.cpp) | 7 | add_library(${PROJECT_NAME} SHARED carddavresource.cpp) |
8 | target_link_libraries(${PROJECT_NAME} sink_webdav_common sink Qt5::Core Qt5::Network KPim::KDAV2) | 8 | target_link_libraries(${PROJECT_NAME} sink_webdav_common sink Qt5::Core Qt5::Network KPim::KDAV2) |
diff --git a/examples/carddavresource/tests/carddavtest.cpp b/examples/carddavresource/tests/carddavtest.cpp index b0f41d1..1d6762d 100644 --- a/examples/carddavresource/tests/carddavtest.cpp +++ b/examples/carddavresource/tests/carddavtest.cpp | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <KDAV2/DavUrl> | 11 | #include <KDAV2/DavUrl> |
12 | #include <KDAV2/DavItemCreateJob> | 12 | #include <KDAV2/DavItemCreateJob> |
13 | #include <KDAV2/DavCollectionsFetchJob> | 13 | #include <KDAV2/DavCollectionsFetchJob> |
14 | #include <KDAV2/DavCollectionCreateJob> | ||
14 | #include <KDAV2/DavCollection> | 15 | #include <KDAV2/DavCollection> |
15 | #include <KContacts/Addressee> | 16 | #include <KContacts/Addressee> |
16 | #include <KContacts/VCardConverter> | 17 | #include <KContacts/VCardConverter> |
@@ -30,28 +31,28 @@ class CardDavTest : public QObject | |||
30 | resource.setProperty("server", "http://localhost/dav/addressbooks/user/doe"); | 31 | resource.setProperty("server", "http://localhost/dav/addressbooks/user/doe"); |
31 | resource.setProperty("username", "doe"); | 32 | resource.setProperty("username", "doe"); |
32 | Sink::SecretStore::instance().insert(resource.identifier(), "doe"); | 33 | Sink::SecretStore::instance().insert(resource.identifier(), "doe"); |
33 | resource.setProperty("testmode", true); | ||
34 | return resource; | 34 | return resource; |
35 | } | 35 | } |
36 | 36 | ||
37 | 37 | ||
38 | QByteArray mResourceInstanceIdentifier; | 38 | QByteArray mResourceInstanceIdentifier; |
39 | 39 | ||
40 | void createContact(const QString &firstname, const QString &lastname) | 40 | void createContact(const QString &firstname, const QString &lastname, const QString &collectionName) |
41 | { | 41 | { |
42 | QUrl mainUrl(QStringLiteral("http://localhost/dav/addressbooks/user/doe")); | 42 | QUrl mainUrl(QStringLiteral("http://localhost/dav/addressbooks/user/doe")); |
43 | mainUrl.setUserName(QStringLiteral("doe")); | 43 | mainUrl.setUserName(QStringLiteral("doe")); |
44 | mainUrl.setPassword(QStringLiteral("doe")); | 44 | mainUrl.setPassword(QStringLiteral("doe")); |
45 | 45 | ||
46 | |||
47 | KDAV2::DavUrl davUrl(mainUrl, KDAV2::CardDav); | 46 | KDAV2::DavUrl davUrl(mainUrl, KDAV2::CardDav); |
48 | 47 | ||
49 | auto *job = new KDAV2::DavCollectionsFetchJob(davUrl); | 48 | auto *job = new KDAV2::DavCollectionsFetchJob(davUrl); |
50 | job->exec(); | 49 | job->exec(); |
51 | 50 | ||
52 | const auto collectionUrl = [&] { | 51 | const auto collectionUrl = [&] { |
53 | if (!job->collections().isEmpty()) { | 52 | for (const auto &col : job->collections()) { |
54 | return job->collections().first().url().url(); | 53 | if (col.displayName() == collectionName) { |
54 | return col.url().url(); | ||
55 | } | ||
55 | } | 56 | } |
56 | return QUrl{}; | 57 | return QUrl{}; |
57 | }(); | 58 | }(); |
@@ -69,11 +70,33 @@ class CardDavTest : public QObject | |||
69 | } | 70 | } |
70 | } | 71 | } |
71 | 72 | ||
73 | void createCollection(const QString &name) | ||
74 | { | ||
75 | QUrl mainUrl(QStringLiteral("http://localhost/dav/addressbooks/user/doe/") + name); | ||
76 | mainUrl.setUserName(QStringLiteral("doe")); | ||
77 | mainUrl.setPassword(QStringLiteral("doe")); | ||
78 | |||
79 | KDAV2::DavUrl davUrl(mainUrl, KDAV2::CardDav); | ||
80 | KDAV2::DavCollection collection{davUrl, name, KDAV2::DavCollection::Contacts}; | ||
81 | |||
82 | auto createJob = new KDAV2::DavCollectionCreateJob(collection); | ||
83 | createJob->exec(); | ||
84 | if (createJob->error()) { | ||
85 | qWarning() << createJob->errorString(); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | void resetTestEnvironment() | ||
90 | { | ||
91 | system("resetmailbox.sh"); | ||
92 | } | ||
93 | |||
72 | private slots: | 94 | private slots: |
73 | 95 | ||
74 | void initTestCase() | 96 | void initTestCase() |
75 | { | 97 | { |
76 | Sink::Test::initTest(); | 98 | Sink::Test::initTest(); |
99 | resetTestEnvironment(); | ||
77 | auto resource = createResource(); | 100 | auto resource = createResource(); |
78 | QVERIFY(!resource.identifier().isEmpty()); | 101 | QVERIFY(!resource.identifier().isEmpty()); |
79 | VERIFYEXEC(Sink::Store::create(resource)); | 102 | VERIFYEXEC(Sink::Store::create(resource)); |
@@ -90,10 +113,25 @@ private slots: | |||
90 | VERIFYEXEC(Sink::ResourceControl::start(mResourceInstanceIdentifier)); | 113 | VERIFYEXEC(Sink::ResourceControl::start(mResourceInstanceIdentifier)); |
91 | } | 114 | } |
92 | 115 | ||
116 | void testSyncAddressbooks() | ||
117 | { | ||
118 | createCollection("addressbook2"); | ||
119 | |||
120 | Sink::SyncScope scope; | ||
121 | scope.setType<Addressbook>(); | ||
122 | scope.resourceFilter(mResourceInstanceIdentifier); | ||
123 | |||
124 | VERIFYEXEC(Sink::Store::synchronize(scope)); | ||
125 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
126 | const auto addressbooks = Sink::Store::read<Addressbook>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); | ||
127 | QCOMPARE(addressbooks.size(), 2); | ||
128 | } | ||
129 | |||
93 | void testSyncContacts() | 130 | void testSyncContacts() |
94 | { | 131 | { |
95 | createContact("john", "doe"); | 132 | createContact("john", "doe", "personal"); |
96 | createContact("jane", "doe"); | 133 | createContact("jane", "doe", "personal"); |
134 | createContact("fred", "durst", "addressbook2"); | ||
97 | Sink::SyncScope scope; | 135 | Sink::SyncScope scope; |
98 | scope.setType<Sink::ApplicationDomain::Contact>(); | 136 | scope.setType<Sink::ApplicationDomain::Contact>(); |
99 | scope.resourceFilter(mResourceInstanceIdentifier); | 137 | scope.resourceFilter(mResourceInstanceIdentifier); |
@@ -101,27 +139,24 @@ private slots: | |||
101 | VERIFYEXEC(Sink::Store::synchronize(scope)); | 139 | VERIFYEXEC(Sink::Store::synchronize(scope)); |
102 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 140 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
103 | const auto contacts = Sink::Store::read<Sink::ApplicationDomain::Contact>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); | 141 | const auto contacts = Sink::Store::read<Sink::ApplicationDomain::Contact>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); |
104 | QCOMPARE(contacts.size(), 2); | 142 | QCOMPARE(contacts.size(), 3); |
105 | 143 | ||
106 | //Ensure a resync works | 144 | //Ensure a resync works |
107 | { | 145 | { |
108 | VERIFYEXEC(Sink::Store::synchronize(scope)); | 146 | VERIFYEXEC(Sink::Store::synchronize(scope)); |
109 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 147 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
110 | const auto contacts = Sink::Store::read<Sink::ApplicationDomain::Contact>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); | 148 | const auto contacts = Sink::Store::read<Sink::ApplicationDomain::Contact>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); |
111 | QCOMPARE(contacts.size(), 2); | 149 | QCOMPARE(contacts.size(), 3); |
112 | } | 150 | } |
113 | } | ||
114 | 151 | ||
115 | void testSyncAddressbooks() | 152 | //Ensure a resync after another creation works |
116 | { | 153 | createContact("alf", "alf", "addressbook2"); |
117 | Sink::SyncScope scope; | 154 | { |
118 | scope.setType<Addressbook>(); | 155 | VERIFYEXEC(Sink::Store::synchronize(scope)); |
119 | scope.resourceFilter(mResourceInstanceIdentifier); | 156 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
120 | 157 | const auto contacts = Sink::Store::read<Sink::ApplicationDomain::Contact>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); | |
121 | VERIFYEXEC(Sink::Store::synchronize(scope)); | 158 | QCOMPARE(contacts.size(), 4); |
122 | VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 159 | } |
123 | const auto addressbooks = Sink::Store::read<Addressbook>(Sink::Query().resourceFilter(mResourceInstanceIdentifier)); | ||
124 | QCOMPARE(addressbooks.size(), 1); | ||
125 | } | 160 | } |
126 | 161 | ||
127 | void testAddContact() | 162 | void testAddContact() |
diff --git a/examples/mailtransportresource/mailtransport.cpp b/examples/mailtransportresource/mailtransport.cpp index ce24d7f..c455b7c 100644 --- a/examples/mailtransportresource/mailtransport.cpp +++ b/examples/mailtransportresource/mailtransport.cpp | |||
@@ -83,7 +83,7 @@ CurlVersionInfo getVersionInfo() | |||
83 | bool sendMessageCurl(const char *to[], int numTos, | 83 | bool sendMessageCurl(const char *to[], int numTos, |
84 | const char *cc[], int numCcs, | 84 | const char *cc[], int numCcs, |
85 | const char *msg, | 85 | const char *msg, |
86 | bool useTls, | 86 | bool useStarttls, |
87 | const char* from, | 87 | const char* from, |
88 | const char *username, const char *password, | 88 | const char *username, const char *password, |
89 | const char *server, bool verifyPeer, const QByteArray &cacert, QByteArray &errorMessage, | 89 | const char *server, bool verifyPeer, const QByteArray &cacert, QByteArray &errorMessage, |
@@ -107,7 +107,7 @@ bool sendMessageCurl(const char *to[], int numTos, | |||
107 | 107 | ||
108 | curl_easy_setopt(curl, CURLOPT_URL, server); | 108 | curl_easy_setopt(curl, CURLOPT_URL, server); |
109 | 109 | ||
110 | if (useTls) { | 110 | if (useStarttls) { |
111 | curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); | 111 | curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); |
112 | } | 112 | } |
113 | 113 | ||
@@ -186,6 +186,7 @@ MailTransport::SendResult MailTransport::sendMessage(const KMime::Message::Ptr & | |||
186 | ccList << mb.address(); | 186 | ccList << mb.address(); |
187 | } | 187 | } |
188 | const bool verifyPeer = options.testFlag(VerifyPeers); | 188 | const bool verifyPeer = options.testFlag(VerifyPeers); |
189 | const bool useStarttls = options.testFlag(UseStarttls); | ||
189 | const bool useTls = options.testFlag(UseTls); | 190 | const bool useTls = options.testFlag(UseTls); |
190 | 191 | ||
191 | const int numTos = toList.size(); | 192 | const int numTos = toList.size(); |
@@ -199,12 +200,21 @@ MailTransport::SendResult MailTransport::sendMessage(const KMime::Message::Ptr & | |||
199 | for (int i = 0; i < numCcs; i++) { | 200 | for (int i = 0; i < numCcs; i++) { |
200 | cc[i] = ccList.at(i); | 201 | cc[i] = ccList.at(i); |
201 | } | 202 | } |
202 | //Because curl will fail with smtps, but it won't tell you why. | ||
203 | auto serverAddress = server; | 203 | auto serverAddress = server; |
204 | serverAddress.replace("smtps://", "smtp://"); | 204 | if (serverAddress.startsWith("smtps://")) { |
205 | serverAddress = serverAddress.mid(8); | ||
206 | } | ||
207 | if (serverAddress.startsWith("smtp://")) { | ||
208 | serverAddress = serverAddress.mid(7); | ||
209 | } | ||
210 | if (useStarttls) { | ||
211 | serverAddress = "smtp://" + serverAddress; | ||
212 | } else if (useTls) { | ||
213 | serverAddress = "smtps://" + serverAddress; | ||
214 | } | ||
205 | 215 | ||
206 | const auto versionInfo = getVersionInfo(); | 216 | const auto versionInfo = getVersionInfo(); |
207 | if (useTls && !versionInfo.supportsSsl) { | 217 | if ((useTls || useStarttls) && !versionInfo.supportsSsl) { |
208 | qCWarning(mailtransportCategory) << "libcurl built without ssl support: " << versionInfo.info; | 218 | qCWarning(mailtransportCategory) << "libcurl built without ssl support: " << versionInfo.info; |
209 | } | 219 | } |
210 | 220 | ||
@@ -212,7 +222,7 @@ MailTransport::SendResult MailTransport::sendMessage(const KMime::Message::Ptr & | |||
212 | QByteArray errorMessage; | 222 | QByteArray errorMessage; |
213 | auto ret = sendMessageCurl(to, numTos, cc, numCcs, | 223 | auto ret = sendMessageCurl(to, numTos, cc, numCcs, |
214 | message->encodedContent(), | 224 | message->encodedContent(), |
215 | useTls, | 225 | useStarttls, |
216 | from.isEmpty() ? nullptr : from, | 226 | from.isEmpty() ? nullptr : from, |
217 | username, password, | 227 | username, password, |
218 | serverAddress, verifyPeer, cacert, | 228 | serverAddress, verifyPeer, cacert, |
diff --git a/examples/mailtransportresource/mailtransport.h b/examples/mailtransportresource/mailtransport.h index 0fa5a66..0f53c2b 100644 --- a/examples/mailtransportresource/mailtransport.h +++ b/examples/mailtransportresource/mailtransport.h | |||
@@ -27,7 +27,8 @@ namespace MailTransport | |||
27 | { | 27 | { |
28 | enum Option { | 28 | enum Option { |
29 | UseTls = 1, | 29 | UseTls = 1, |
30 | VerifyPeers = 2 | 30 | UseStarttls = 2, |
31 | VerifyPeers = 4 | ||
31 | }; | 32 | }; |
32 | Q_DECLARE_FLAGS(Options, Option); | 33 | Q_DECLARE_FLAGS(Options, Option); |
33 | 34 | ||
diff --git a/examples/mailtransportresource/mailtransportresource.cpp b/examples/mailtransportresource/mailtransportresource.cpp index 10d94bc..9163d3b 100644 --- a/examples/mailtransportresource/mailtransportresource.cpp +++ b/examples/mailtransportresource/mailtransportresource.cpp | |||
@@ -127,7 +127,11 @@ public: | |||
127 | } else { | 127 | } else { |
128 | MailTransport::Options options; | 128 | MailTransport::Options options; |
129 | if (settings.server.contains("smtps")) { | 129 | if (settings.server.contains("smtps")) { |
130 | options |= MailTransport::UseTls; | 130 | if (settings.server.contains("465")) { |
131 | options |= MailTransport::UseTls; | ||
132 | } else { | ||
133 | options |= MailTransport::UseStarttls; | ||
134 | } | ||
131 | } | 135 | } |
132 | 136 | ||
133 | SinkLog() << "Sending message " << settings.server << settings.username << "CaCert: " << settings.cacert << "Using tls: " << bool(options & MailTransport::UseTls); | 137 | SinkLog() << "Sending message " << settings.server << settings.username << "CaCert: " << settings.cacert << "Using tls: " << bool(options & MailTransport::UseTls); |
diff --git a/examples/webdavcommon/CMakeLists.txt b/examples/webdavcommon/CMakeLists.txt index c4e99f2..51bf151 100644 --- a/examples/webdavcommon/CMakeLists.txt +++ b/examples/webdavcommon/CMakeLists.txt | |||
@@ -2,7 +2,7 @@ project(sink_webdav_common) | |||
2 | 2 | ||
3 | set(CMAKE_CXX_STANDARD 14) | 3 | set(CMAKE_CXX_STANDARD 14) |
4 | 4 | ||
5 | find_package(KPimKDAV2 REQUIRED) | 5 | find_package(KPimKDAV2 REQUIRED 0.2.0) |
6 | 6 | ||
7 | add_library(${PROJECT_NAME} STATIC webdav.cpp) | 7 | add_library(${PROJECT_NAME} STATIC webdav.cpp) |
8 | target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Network KPim::KDAV2 sink) | 8 | target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Network KPim::KDAV2 sink) |
diff --git a/examples/webdavcommon/webdav.cpp b/examples/webdavcommon/webdav.cpp index 904e1e2..0dfb6c0 100644 --- a/examples/webdavcommon/webdav.cpp +++ b/examples/webdavcommon/webdav.cpp | |||
@@ -89,11 +89,11 @@ static KAsync::Job<T> runJob(KJob *job, const std::function<T(KJob *)> &func) | |||
89 | } | 89 | } |
90 | 90 | ||
91 | WebDavSynchronizer::WebDavSynchronizer(const Sink::ResourceContext &context, | 91 | WebDavSynchronizer::WebDavSynchronizer(const Sink::ResourceContext &context, |
92 | KDAV2::Protocol protocol, QByteArray collectionName, QByteArray itemName) | 92 | KDAV2::Protocol protocol, QByteArray mCollectionType, QByteArray mEntityType) |
93 | : Sink::Synchronizer(context), | 93 | : Sink::Synchronizer(context), |
94 | protocol(protocol), | 94 | protocol(protocol), |
95 | collectionName(std::move(collectionName)), | 95 | mCollectionType(std::move(mCollectionType)), |
96 | itemName(std::move(itemName)) | 96 | mEntityType(std::move(mEntityType)) |
97 | { | 97 | { |
98 | auto config = ResourceConfig::getConfiguration(context.instanceId()); | 98 | auto config = ResourceConfig::getConfiguration(context.instanceId()); |
99 | 99 | ||
@@ -111,15 +111,16 @@ QList<Sink::Synchronizer::SyncRequest> WebDavSynchronizer::getSyncRequests(const | |||
111 | // We want to synchronize everything | 111 | // We want to synchronize everything |
112 | 112 | ||
113 | // Item synchronization does the collections anyway | 113 | // Item synchronization does the collections anyway |
114 | // list << Synchronizer::SyncRequest{ Sink::QueryBase(collectionName) }; | 114 | // list << Synchronizer::SyncRequest{ Sink::QueryBase(mCollectionType) }; |
115 | list << Synchronizer::SyncRequest{ Sink::QueryBase(itemName) }; | 115 | list << Synchronizer::SyncRequest{ Sink::QueryBase(mEntityType) }; |
116 | } | 116 | } |
117 | return list; | 117 | return list; |
118 | } | 118 | } |
119 | 119 | ||
120 | KAsync::Job<void> WebDavSynchronizer::synchronizeWithSource(const Sink::QueryBase &query) | 120 | KAsync::Job<void> WebDavSynchronizer::synchronizeWithSource(const Sink::QueryBase &query) |
121 | { | 121 | { |
122 | if (query.type() != collectionName && query.type() != itemName) { | 122 | if (query.type() != mCollectionType && query.type() != mEntityType) { |
123 | SinkWarning() << "Received synchronization reuqest with unkown type" << query; | ||
123 | return KAsync::null<void>(); | 124 | return KAsync::null<void>(); |
124 | } | 125 | } |
125 | 126 | ||
@@ -133,10 +134,10 @@ KAsync::Job<void> WebDavSynchronizer::synchronizeWithSource(const Sink::QueryBas | |||
133 | return collections; | 134 | return collections; |
134 | }); | 135 | }); |
135 | 136 | ||
136 | if (query.type() == collectionName) { | 137 | if (query.type() == mCollectionType) { |
137 | // Do nothing more | 138 | // Do nothing more |
138 | return job; | 139 | return job; |
139 | } else if (query.type() == itemName) { | 140 | } else if (query.type() == mEntityType) { |
140 | auto progress = QSharedPointer<int>::create(0); | 141 | auto progress = QSharedPointer<int>::create(0); |
141 | auto total = QSharedPointer<int>::create(0); | 142 | auto total = QSharedPointer<int>::create(0); |
142 | 143 | ||
@@ -156,17 +157,24 @@ KAsync::Job<void> WebDavSynchronizer::synchronizeWithSource(const Sink::QueryBas | |||
156 | return KAsync::null<void>(); | 157 | return KAsync::null<void>(); |
157 | } | 158 | } |
158 | 159 | ||
159 | SinkTrace() << "Syncing collection:" << collectionResourceID; | 160 | SinkTrace() << "Syncing collection:" << collectionResourceID << collection.displayName(); |
160 | auto itemsResourceIDs = QSharedPointer<QSet<QByteArray>>::create(); | 161 | auto itemsResourceIDs = QSharedPointer<QSet<QByteArray>>::create(); |
161 | return synchronizeCollection(collection, progress, total, itemsResourceIDs) | 162 | return synchronizeCollection(collection, progress, total, itemsResourceIDs) |
162 | .then([=] { | 163 | .then([=] { |
163 | scanForRemovals(itemName, [&itemsResourceIDs](const QByteArray &remoteId) { | 164 | const auto collectionLocalId = collectionLocalResourceID(collection); |
164 | return itemsResourceIDs->contains(remoteId); | 165 | scanForRemovals(mEntityType, |
165 | }); | 166 | [&](const std::function<void(const QByteArray &)> &callback) { |
167 | //FIXME: The collection type just happens to have the same name as the parent collection property | ||
168 | const auto collectionProperty = mCollectionType; | ||
169 | store().indexLookup(mEntityType, collectionProperty, collectionLocalId, callback); | ||
170 | }, | ||
171 | [&itemsResourceIDs](const QByteArray &remoteId) { | ||
172 | return itemsResourceIDs->contains(remoteId); | ||
173 | }); | ||
166 | }); | 174 | }); |
167 | }) | 175 | }) |
168 | .then([=]() { | 176 | .then([=]() { |
169 | scanForRemovals(collectionName, [&collectionResourceIDs](const QByteArray &remoteId) { | 177 | scanForRemovals(mCollectionType, [&collectionResourceIDs](const QByteArray &remoteId) { |
170 | return collectionResourceIDs->contains(remoteId); | 178 | return collectionResourceIDs->contains(remoteId); |
171 | }); | 179 | }); |
172 | }); | 180 | }); |
diff --git a/examples/webdavcommon/webdav.h b/examples/webdavcommon/webdav.h index 813da60..af6c47e 100644 --- a/examples/webdavcommon/webdav.h +++ b/examples/webdavcommon/webdav.h | |||
@@ -109,8 +109,8 @@ protected: | |||
109 | 109 | ||
110 | private: | 110 | private: |
111 | KDAV2::Protocol protocol; | 111 | KDAV2::Protocol protocol; |
112 | const QByteArray collectionName; | 112 | const QByteArray mCollectionType; |
113 | const QByteArray itemName; | 113 | const QByteArray mEntityType; |
114 | 114 | ||
115 | QUrl mServer; | 115 | QUrl mServer; |
116 | QString mUsername; | 116 | QString mUsername; |
diff --git a/tests/dbwriter.cpp b/tests/dbwriter.cpp index 3045eac..a25faec 100644 --- a/tests/dbwriter.cpp +++ b/tests/dbwriter.cpp | |||
@@ -29,14 +29,14 @@ int main(int argc, char *argv[]) | |||
29 | qWarning() << "No valid transaction"; | 29 | qWarning() << "No valid transaction"; |
30 | return -1; | 30 | return -1; |
31 | } | 31 | } |
32 | transaction.openDatabase("a", nullptr, false).write(QByteArray::number(i), "a"); | 32 | transaction.openDatabase("a", nullptr, 0).write(QByteArray::number(i), "a"); |
33 | transaction.openDatabase("b", nullptr, false).write(QByteArray::number(i), "b"); | 33 | transaction.openDatabase("b", nullptr, 0).write(QByteArray::number(i), "b"); |
34 | transaction.openDatabase("c", nullptr, false).write(QByteArray::number(i), "c"); | 34 | transaction.openDatabase("c", nullptr, 0).write(QByteArray::number(i), "c"); |
35 | transaction.openDatabase("p", nullptr, false).write(QByteArray::number(i), "c"); | 35 | transaction.openDatabase("p", nullptr, 0).write(QByteArray::number(i), "c"); |
36 | transaction.openDatabase("q", nullptr, false).write(QByteArray::number(i), "c"); | 36 | transaction.openDatabase("q", nullptr, 0).write(QByteArray::number(i), "c"); |
37 | if (i > (count/2)) { | 37 | if (i > (count/2)) { |
38 | for (int d = 0; d < 40; d++) { | 38 | for (int d = 0; d < 40; d++) { |
39 | transaction.openDatabase("db" + QByteArray::number(d), nullptr, false).write(QByteArray::number(i), "a"); | 39 | transaction.openDatabase("db" + QByteArray::number(d), nullptr, 0).write(QByteArray::number(i), "a"); |
40 | } | 40 | } |
41 | } | 41 | } |
42 | if ((i % 1000) == 0) { | 42 | if ((i % 1000) == 0) { |
diff --git a/tests/pipelinetest.cpp b/tests/pipelinetest.cpp index 34ee3a5..801a9e0 100644 --- a/tests/pipelinetest.cpp +++ b/tests/pipelinetest.cpp | |||
@@ -32,10 +32,10 @@ static QList<Sink::Storage::Key> getKeys(const QByteArray &dbEnv, const QByteArr | |||
32 | { | 32 | { |
33 | Sink::Storage::DataStore store(Sink::storageLocation(), dbEnv, Sink::Storage::DataStore::ReadOnly); | 33 | Sink::Storage::DataStore store(Sink::storageLocation(), dbEnv, Sink::Storage::DataStore::ReadOnly); |
34 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 34 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
35 | auto db = transaction.openDatabase(name, nullptr, false); | 35 | auto db = transaction.openDatabase(name, nullptr, Sink::Storage::IntegerKeys); |
36 | QList<Sink::Storage::Key> result; | 36 | QList<Sink::Storage::Key> result; |
37 | db.scan("", [&](const QByteArray &key, const QByteArray &value) { | 37 | db.scan("", [&](const QByteArray &key, const QByteArray &value) { |
38 | size_t revision = Sink::byteArrayToSizeT(value); | 38 | size_t revision = Sink::byteArrayToSizeT(key); |
39 | result << Sink::Storage::Key(Sink::Storage::Identifier::fromDisplayByteArray( | 39 | result << Sink::Storage::Key(Sink::Storage::Identifier::fromDisplayByteArray( |
40 | Sink::Storage::DataStore::getUidFromRevision(transaction, revision)), | 40 | Sink::Storage::DataStore::getUidFromRevision(transaction, revision)), |
41 | revision); | 41 | revision); |
@@ -48,7 +48,7 @@ static QByteArray getEntity(const QByteArray &dbEnv, const QByteArray &name, con | |||
48 | { | 48 | { |
49 | Sink::Storage::DataStore store(Sink::storageLocation(), dbEnv, Sink::Storage::DataStore::ReadOnly); | 49 | Sink::Storage::DataStore store(Sink::storageLocation(), dbEnv, Sink::Storage::DataStore::ReadOnly); |
50 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 50 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
51 | auto db = transaction.openDatabase(name, nullptr, false); | 51 | auto db = transaction.openDatabase(name, nullptr, Sink::Storage::IntegerKeys); |
52 | QByteArray result; | 52 | QByteArray result; |
53 | db.scan(key.revision().toSizeT(), [&](size_t rev, const QByteArray &value) { | 53 | db.scan(key.revision().toSizeT(), [&](size_t rev, const QByteArray &value) { |
54 | result = value; | 54 | result = value; |