diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/CMakeLists.txt | 4 | ||||
-rw-r--r-- | common/asyncutils.h | 2 | ||||
-rw-r--r-- | common/definitions.cpp | 18 | ||||
-rw-r--r-- | common/definitions.h | 8 | ||||
-rw-r--r-- | common/domain/applicationdomaintype.cpp | 17 | ||||
-rw-r--r-- | common/domain/applicationdomaintype.h | 15 | ||||
-rw-r--r-- | common/domain/applicationdomaintype_p.h | 2 | ||||
-rw-r--r-- | common/log.cpp | 50 | ||||
-rw-r--r-- | common/mail/threadindexer.cpp | 30 | ||||
-rw-r--r-- | common/mailpreprocessor.cpp | 22 | ||||
-rw-r--r-- | common/modelresult.cpp | 10 | ||||
-rw-r--r-- | common/notification.cpp | 2 | ||||
-rw-r--r-- | common/notifier.cpp | 4 | ||||
-rw-r--r-- | common/resource.cpp | 8 | ||||
-rw-r--r-- | common/resourceaccess.cpp | 2 | ||||
-rw-r--r-- | common/resourcefacade.cpp | 53 | ||||
-rw-r--r-- | common/resourcefacade.h | 1 | ||||
-rw-r--r-- | common/storage.h | 6 | ||||
-rw-r--r-- | common/storage/entitystore.cpp | 12 | ||||
-rw-r--r-- | common/storage_common.cpp | 12 | ||||
-rw-r--r-- | common/storage_lmdb.cpp | 31 | ||||
-rw-r--r-- | common/store.cpp | 44 | ||||
-rw-r--r-- | common/store.h | 3 | ||||
-rw-r--r-- | common/synchronizer.cpp | 19 |
24 files changed, 271 insertions, 104 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 001a412..8421fc2 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt | |||
@@ -116,13 +116,13 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} | |||
116 | EXPORT_NAME ${PROJECT_NAME} | 116 | EXPORT_NAME ${PROJECT_NAME} |
117 | ) | 117 | ) |
118 | 118 | ||
119 | qt5_use_modules(${PROJECT_NAME} LINK_PUBLIC Network) | ||
120 | qt5_use_modules(${PROJECT_NAME} LINK_PRIVATE Gui) | ||
121 | target_link_libraries(${PROJECT_NAME} | 119 | target_link_libraries(${PROJECT_NAME} |
122 | PUBLIC | 120 | PUBLIC |
123 | KAsync | 121 | KAsync |
122 | Qt5::Network | ||
124 | PRIVATE | 123 | PRIVATE |
125 | ${storage_LIBS} | 124 | ${storage_LIBS} |
125 | Qt5::Gui | ||
126 | KF5::Mime | 126 | KF5::Mime |
127 | KF5::Contacts | 127 | KF5::Contacts |
128 | ) | 128 | ) |
diff --git a/common/asyncutils.h b/common/asyncutils.h index 67b5928..c80af30 100644 --- a/common/asyncutils.h +++ b/common/asyncutils.h | |||
@@ -31,12 +31,12 @@ KAsync::Job<T> run(const std::function<T()> &f, bool runAsync = true) | |||
31 | return KAsync::start<T>([f](KAsync::Future<T> &future) { | 31 | return KAsync::start<T>([f](KAsync::Future<T> &future) { |
32 | auto result = QtConcurrent::run(f); | 32 | auto result = QtConcurrent::run(f); |
33 | auto watcher = new QFutureWatcher<T>; | 33 | auto watcher = new QFutureWatcher<T>; |
34 | watcher->setFuture(result); | ||
35 | QObject::connect(watcher, &QFutureWatcher<T>::finished, watcher, [&future, watcher]() { | 34 | QObject::connect(watcher, &QFutureWatcher<T>::finished, watcher, [&future, watcher]() { |
36 | future.setValue(watcher->future().result()); | 35 | future.setValue(watcher->future().result()); |
37 | delete watcher; | 36 | delete watcher; |
38 | future.setFinished(); | 37 | future.setFinished(); |
39 | }); | 38 | }); |
39 | watcher->setFuture(result); | ||
40 | }); | 40 | }); |
41 | } else { | 41 | } else { |
42 | return KAsync::start<T>([f]() { | 42 | return KAsync::start<T>([f]() { |
diff --git a/common/definitions.cpp b/common/definitions.cpp index 17977bc..ee18d52 100644 --- a/common/definitions.cpp +++ b/common/definitions.cpp | |||
@@ -39,11 +39,17 @@ QString Sink::storageLocation() | |||
39 | return dataLocation() + "/storage"; | 39 | return dataLocation() + "/storage"; |
40 | } | 40 | } |
41 | 41 | ||
42 | static QString sinkLocation(QStandardPaths::StandardLocation location) | ||
43 | { | ||
44 | return QStandardPaths::writableLocation(location) + "/sink"; | ||
45 | } | ||
46 | |||
42 | QString Sink::dataLocation() | 47 | QString Sink::dataLocation() |
43 | { | 48 | { |
44 | static QString location; | 49 | static QString location = sinkLocation(QStandardPaths::GenericDataLocation); |
50 | //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization). | ||
45 | if (rereadDataLocation) { | 51 | if (rereadDataLocation) { |
46 | location = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/sink"; | 52 | location = sinkLocation(QStandardPaths::GenericDataLocation); |
47 | rereadDataLocation = false; | 53 | rereadDataLocation = false; |
48 | } | 54 | } |
49 | return location; | 55 | return location; |
@@ -51,9 +57,10 @@ QString Sink::dataLocation() | |||
51 | 57 | ||
52 | QString Sink::configLocation() | 58 | QString Sink::configLocation() |
53 | { | 59 | { |
54 | static QString location; | 60 | static QString location = sinkLocation(QStandardPaths::GenericConfigLocation); |
61 | //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization). | ||
55 | if (rereadConfigLocation) { | 62 | if (rereadConfigLocation) { |
56 | location = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/sink"; | 63 | location = sinkLocation(QStandardPaths::GenericConfigLocation); |
57 | rereadConfigLocation = false; | 64 | rereadConfigLocation = false; |
58 | } | 65 | } |
59 | return location; | 66 | return location; |
@@ -61,8 +68,9 @@ QString Sink::configLocation() | |||
61 | 68 | ||
62 | QString Sink::temporaryFileLocation() | 69 | QString Sink::temporaryFileLocation() |
63 | { | 70 | { |
64 | static QString location; | 71 | static QString location = dataLocation() + "/temporaryFiles"; |
65 | static bool dirCreated = false; | 72 | static bool dirCreated = false; |
73 | //Warning: This is not threadsafe, but clearLocationCache is only ever used in testcode. The initialization above is required to make at least the initialization threadsafe (relies on C++11 threadsafe initialization). | ||
66 | if (rereadTemporaryFileLocation) { | 74 | if (rereadTemporaryFileLocation) { |
67 | location = dataLocation() + "/temporaryFiles"; | 75 | location = dataLocation() + "/temporaryFiles"; |
68 | dirCreated = QDir{}.mkpath(location); | 76 | dirCreated = QDir{}.mkpath(location); |
diff --git a/common/definitions.h b/common/definitions.h index ce9e794..7ef215b 100644 --- a/common/definitions.h +++ b/common/definitions.h | |||
@@ -25,10 +25,16 @@ | |||
25 | #include <QByteArray> | 25 | #include <QByteArray> |
26 | 26 | ||
27 | namespace Sink { | 27 | namespace Sink { |
28 | void SINK_EXPORT clearLocationCache(); | ||
29 | QString SINK_EXPORT storageLocation(); | 28 | QString SINK_EXPORT storageLocation(); |
30 | QString SINK_EXPORT dataLocation(); | 29 | QString SINK_EXPORT dataLocation(); |
31 | QString SINK_EXPORT configLocation(); | 30 | QString SINK_EXPORT configLocation(); |
32 | QString SINK_EXPORT temporaryFileLocation(); | 31 | QString SINK_EXPORT temporaryFileLocation(); |
33 | QString SINK_EXPORT resourceStorageLocation(const QByteArray &resourceInstanceIdentifier); | 32 | QString SINK_EXPORT resourceStorageLocation(const QByteArray &resourceInstanceIdentifier); |
33 | |||
34 | /** | ||
35 | * Clear the location cache and lookup locations again. | ||
36 | * | ||
37 | * Warning: Calling this results in non-threadsafe initialization, only use it in test-code. | ||
38 | */ | ||
39 | void SINK_EXPORT clearLocationCache(); | ||
34 | } | 40 | } |
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp index 3718f77..ee70c35 100644 --- a/common/domain/applicationdomaintype.cpp +++ b/common/domain/applicationdomaintype.cpp | |||
@@ -34,7 +34,13 @@ QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDom | |||
34 | QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::ApplicationDomainType &type) | 34 | QDebug Sink::ApplicationDomain::operator<< (QDebug d, const Sink::ApplicationDomain::ApplicationDomainType &type) |
35 | { | 35 | { |
36 | d << "ApplicationDomainType(\n"; | 36 | d << "ApplicationDomainType(\n"; |
37 | auto properties = type.mAdaptor->availableProperties(); | 37 | auto properties = [&] { |
38 | if (!type.changedProperties().isEmpty()) { | ||
39 | return type.changedProperties(); | ||
40 | } else { | ||
41 | return type.mAdaptor->availableProperties(); | ||
42 | } | ||
43 | }(); | ||
38 | std::sort(properties.begin(), properties.end()); | 44 | std::sort(properties.begin(), properties.end()); |
39 | d << " " << "Id: " << "\t" << type.identifier() << "\n"; | 45 | d << " " << "Id: " << "\t" << type.identifier() << "\n"; |
40 | d << " " << "Resource: " << "\t" << type.resourceInstanceIdentifier() << "\n"; | 46 | d << " " << "Resource: " << "\t" << type.resourceInstanceIdentifier() << "\n"; |
@@ -216,8 +222,13 @@ QVariant ApplicationDomainType::getProperty(const QByteArray &key) const | |||
216 | void ApplicationDomainType::setProperty(const QByteArray &key, const QVariant &value) | 222 | void ApplicationDomainType::setProperty(const QByteArray &key, const QVariant &value) |
217 | { | 223 | { |
218 | Q_ASSERT(mAdaptor); | 224 | Q_ASSERT(mAdaptor); |
219 | mChangeSet->insert(key); | 225 | auto existing = mAdaptor->getProperty(key); |
220 | mAdaptor->setProperty(key, value); | 226 | if (existing.isValid() && existing == value) { |
227 | SinkTrace() << "Tried to set property that is still the same: " << key << value; | ||
228 | } else { | ||
229 | mChangeSet->insert(key); | ||
230 | mAdaptor->setProperty(key, value); | ||
231 | } | ||
221 | } | 232 | } |
222 | 233 | ||
223 | void ApplicationDomainType::setResource(const QByteArray &identifier) | 234 | void ApplicationDomainType::setResource(const QByteArray &identifier) |
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index 602d54c..f7fd07e 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h | |||
@@ -101,6 +101,7 @@ enum SINK_EXPORT ErrorCode { | |||
101 | LoginError, | 101 | LoginError, |
102 | ConfigurationError, | 102 | ConfigurationError, |
103 | TransmissionError, | 103 | TransmissionError, |
104 | ConnectionLostError, | ||
104 | }; | 105 | }; |
105 | 106 | ||
106 | enum SINK_EXPORT SuccessCode { | 107 | enum SINK_EXPORT SuccessCode { |
@@ -118,12 +119,14 @@ enum SINK_EXPORT SyncStatus { | |||
118 | * The status of an account or resource. | 119 | * The status of an account or resource. |
119 | * | 120 | * |
120 | * It is set as follows: | 121 | * It is set as follows: |
121 | * * By default the status is offline. | 122 | * * By default the status is no status. |
123 | * * If a connection to the server failed the status is Offline. | ||
122 | * * If a connection to the server could be established the status is Connected. | 124 | * * If a connection to the server could be established the status is Connected. |
123 | * * If an error occurred that keeps the resource from operating (so non transient), the resource enters the error state. | 125 | * * If an error occurred that keeps the resource from operating (so non transient), the resource enters the error state. |
124 | * * If a long running operation is started the resource goes to the busy state (and return to the previous state after that). | 126 | * * If a long running operation is started the resource goes to the busy state (and return to the previous state after that). |
125 | */ | 127 | */ |
126 | enum SINK_EXPORT Status { | 128 | enum SINK_EXPORT Status { |
129 | NoStatus, | ||
127 | OfflineStatus, | 130 | OfflineStatus, |
128 | ConnectedStatus, | 131 | ConnectedStatus, |
129 | BusyStatus, | 132 | BusyStatus, |
@@ -270,7 +273,17 @@ public: | |||
270 | bool hasProperty(const QByteArray &key) const; | 273 | bool hasProperty(const QByteArray &key) const; |
271 | 274 | ||
272 | QVariant getProperty(const QByteArray &key) const; | 275 | QVariant getProperty(const QByteArray &key) const; |
276 | |||
277 | /** | ||
278 | * Set a property and record a changed property | ||
279 | * | ||
280 | * If the propery is available and did not change the call will be ignored. | ||
281 | */ | ||
273 | void setProperty(const QByteArray &key, const QVariant &value); | 282 | void setProperty(const QByteArray &key, const QVariant &value); |
283 | |||
284 | /** | ||
285 | * Convenience method to set a reference property. | ||
286 | */ | ||
274 | void setProperty(const QByteArray &key, const ApplicationDomainType &value); | 287 | void setProperty(const QByteArray &key, const ApplicationDomainType &value); |
275 | 288 | ||
276 | QByteArray getBlobProperty(const QByteArray &key) const; | 289 | QByteArray getBlobProperty(const QByteArray &key) const; |
diff --git a/common/domain/applicationdomaintype_p.h b/common/domain/applicationdomaintype_p.h index a5a6b1d..a60df38 100644 --- a/common/domain/applicationdomaintype_p.h +++ b/common/domain/applicationdomaintype_p.h | |||
@@ -45,5 +45,7 @@ struct TypeHelper { | |||
45 | } else { | 45 | } else { |
46 | Q_ASSERT(false); | 46 | Q_ASSERT(false); |
47 | } | 47 | } |
48 | //Silence compiler warning | ||
49 | return Func<Sink::ApplicationDomain::Mail>{}(std::forward<Args...>(args...)); | ||
48 | } | 50 | } |
49 | }; | 51 | }; |
diff --git a/common/log.cpp b/common/log.cpp index 5dfb872..bfc9d5e 100644 --- a/common/log.cpp +++ b/common/log.cpp | |||
@@ -10,7 +10,6 @@ | |||
10 | #include <QMutexLocker> | 10 | #include <QMutexLocker> |
11 | #include <iostream> | 11 | #include <iostream> |
12 | #include <unistd.h> | 12 | #include <unistd.h> |
13 | #include <memory> | ||
14 | #include <atomic> | 13 | #include <atomic> |
15 | #include <definitions.h> | 14 | #include <definitions.h> |
16 | #include <QThreadStorage> | 15 | #include <QThreadStorage> |
@@ -27,10 +26,13 @@ static QSettings &config() | |||
27 | return *sSettings.localData(); | 26 | return *sSettings.localData(); |
28 | } | 27 | } |
29 | 28 | ||
30 | static QByteArray sPrimaryComponent; | 29 | Q_GLOBAL_STATIC(QByteArray, sPrimaryComponent); |
30 | |||
31 | void Sink::Log::setPrimaryComponent(const QString &component) | 31 | void Sink::Log::setPrimaryComponent(const QString &component) |
32 | { | 32 | { |
33 | sPrimaryComponent = component.toUtf8(); | 33 | if (!sPrimaryComponent.isDestroyed()) { |
34 | *sPrimaryComponent = component.toUtf8(); | ||
35 | } | ||
34 | } | 36 | } |
35 | 37 | ||
36 | class DebugStream : public QIODevice | 38 | class DebugStream : public QIODevice |
@@ -267,16 +269,21 @@ public: | |||
267 | QSet<QString> mDebugAreas; | 269 | QSet<QString> mDebugAreas; |
268 | }; | 270 | }; |
269 | 271 | ||
270 | static auto sDebugAreaCollector = std::unique_ptr<DebugAreaCollector>(new DebugAreaCollector); | 272 | Q_GLOBAL_STATIC(DebugAreaCollector, sDebugAreaCollector); |
271 | 273 | ||
272 | QSet<QString> Sink::Log::debugAreas() | 274 | QSet<QString> Sink::Log::debugAreas() |
273 | { | 275 | { |
274 | return sDebugAreaCollector->debugAreas(); | 276 | if (!sDebugAreaCollector.isDestroyed()) { |
277 | return sDebugAreaCollector->debugAreas(); | ||
278 | } | ||
279 | return {}; | ||
275 | } | 280 | } |
276 | 281 | ||
277 | static void collectDebugArea(const QString &debugArea) | 282 | static void collectDebugArea(const QString &debugArea) |
278 | { | 283 | { |
279 | sDebugAreaCollector->add(debugArea); | 284 | if (!sDebugAreaCollector.isDestroyed()) { |
285 | sDebugAreaCollector->add(debugArea); | ||
286 | } | ||
280 | } | 287 | } |
281 | 288 | ||
282 | static bool containsItemStartingWith(const QByteArray &pattern, const QByteArrayList &list) | 289 | static bool containsItemStartingWith(const QByteArray &pattern, const QByteArrayList &list) |
@@ -318,13 +325,17 @@ static QByteArray getFileName(const char *file) | |||
318 | 325 | ||
319 | static QString assembleDebugArea(const char *debugArea, const char *debugComponent, const char *file) | 326 | static QString assembleDebugArea(const char *debugArea, const char *debugComponent, const char *file) |
320 | { | 327 | { |
321 | if (sPrimaryComponent.isEmpty()) { | 328 | if (!sPrimaryComponent.isDestroyed() && sPrimaryComponent->isEmpty()) { |
322 | sPrimaryComponent = getProgramName(); | 329 | *sPrimaryComponent = getProgramName(); |
330 | } | ||
331 | if (!sPrimaryComponent.isDestroyed()) { | ||
332 | //Using stringbuilder for fewer allocations | ||
333 | return QLatin1String{*sPrimaryComponent} % QLatin1String{"."} % | ||
334 | (debugComponent ? (QLatin1String{debugComponent} + QLatin1String{"."}) : QLatin1String{""}) % | ||
335 | (debugArea ? QLatin1String{debugArea} : QLatin1String{getFileName(file)}); | ||
336 | } else { | ||
337 | return {}; | ||
323 | } | 338 | } |
324 | //Using stringbuilder for fewer allocations | ||
325 | return QLatin1String{sPrimaryComponent} % QLatin1String{"."} % | ||
326 | (debugComponent ? (QLatin1String{debugComponent} + QLatin1String{"."}) : QLatin1String{""}) % | ||
327 | (debugArea ? QLatin1String{debugArea} : QLatin1String{getFileName(file)}); | ||
328 | } | 339 | } |
329 | 340 | ||
330 | static bool isFiltered(DebugLevel debugLevel, const QByteArray &fullDebugArea) | 341 | static bool isFiltered(DebugLevel debugLevel, const QByteArray &fullDebugArea) |
@@ -346,14 +357,19 @@ bool Sink::Log::isFiltered(DebugLevel debugLevel, const char *debugArea, const c | |||
346 | return isFiltered(debugLevel, assembleDebugArea(debugArea, debugComponent, file).toLatin1()); | 357 | return isFiltered(debugLevel, assembleDebugArea(debugArea, debugComponent, file).toLatin1()); |
347 | } | 358 | } |
348 | 359 | ||
360 | Q_GLOBAL_STATIC(NullStream, sNullStream); | ||
361 | Q_GLOBAL_STATIC(DebugStream, sDebugStream); | ||
362 | |||
349 | QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent) | 363 | QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, const char *function, const char *debugArea, const char *debugComponent) |
350 | { | 364 | { |
351 | const auto fullDebugArea = assembleDebugArea(debugArea, debugComponent, file); | 365 | const auto fullDebugArea = assembleDebugArea(debugArea, debugComponent, file); |
352 | collectDebugArea(fullDebugArea); | 366 | collectDebugArea(fullDebugArea); |
353 | 367 | ||
354 | static NullStream nullstream; | ||
355 | if (isFiltered(debugLevel, fullDebugArea.toLatin1())) { | 368 | if (isFiltered(debugLevel, fullDebugArea.toLatin1())) { |
356 | return QDebug(&nullstream); | 369 | if (!sNullStream.isDestroyed()) { |
370 | return QDebug(sNullStream); | ||
371 | } | ||
372 | return QDebug{QtDebugMsg}; | ||
357 | } | 373 | } |
358 | 374 | ||
359 | QString prefix; | 375 | QString prefix; |
@@ -418,8 +434,10 @@ QDebug Sink::Log::debugStream(DebugLevel debugLevel, int line, const char *file, | |||
418 | } | 434 | } |
419 | output += ":"; | 435 | output += ":"; |
420 | 436 | ||
421 | static DebugStream stream; | 437 | if (sDebugStream.isDestroyed()) { |
422 | QDebug debug(&stream); | 438 | return QDebug{QtDebugMsg}; |
439 | } | ||
440 | QDebug debug(sDebugStream); | ||
423 | 441 | ||
424 | debug.noquote().nospace() << output; | 442 | debug.noquote().nospace() << output; |
425 | 443 | ||
diff --git a/common/mail/threadindexer.cpp b/common/mail/threadindexer.cpp index 4171d85..1401fc8 100644 --- a/common/mail/threadindexer.cpp +++ b/common/mail/threadindexer.cpp | |||
@@ -34,9 +34,37 @@ void ThreadIndexer::updateThreadingIndex(const QByteArray &identifier, const App | |||
34 | 34 | ||
35 | QVector<QByteArray> thread; | 35 | QVector<QByteArray> thread; |
36 | 36 | ||
37 | //a child already registered our thread. | 37 | //check if a child already registered our thread. |
38 | thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId); | 38 | thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId); |
39 | 39 | ||
40 | if (!thread.isEmpty()) { | ||
41 | //A child already registered our thread so we merge the childs thread | ||
42 | //* check if we have a parent thread, if not just continue as usual | ||
43 | //* get all messages that have the same threadid as the child | ||
44 | //* switch all to the parents thread | ||
45 | auto parentThread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); | ||
46 | if (!parentThread.isEmpty()) { | ||
47 | auto childThreadId = thread.first(); | ||
48 | auto parentThreadId = parentThread.first(); | ||
49 | SinkTrace() << "Merging child thread: " << childThreadId << " into parent thread: " << parentThreadId; | ||
50 | |||
51 | //Ensure this mail ends up in the correct thread | ||
52 | index().unindex<Mail::MessageId, Mail::ThreadId>(messageId, childThreadId, transaction); | ||
53 | //We have to copy the id here, otherwise it doesn't survive the subsequent writes | ||
54 | thread = QVector<QByteArray>() << QByteArray{parentThreadId.data(), parentThreadId.size()}; | ||
55 | |||
56 | //Merge all child messages into the correct thread | ||
57 | auto childThreadMessageIds = index().secondaryLookup<Mail::ThreadId, Mail::MessageId>(childThreadId); | ||
58 | for (const auto &msgId : childThreadMessageIds) { | ||
59 | SinkTrace() << "Merging child message: " << msgId; | ||
60 | index().unindex<Mail::MessageId, Mail::ThreadId>(msgId, childThreadId, transaction); | ||
61 | index().unindex<Mail::ThreadId, Mail::MessageId>(childThreadId, msgId, transaction); | ||
62 | index().index<Mail::MessageId, Mail::ThreadId>(msgId, parentThreadId, transaction); | ||
63 | index().index<Mail::ThreadId, Mail::MessageId>(parentThreadId, msgId, transaction); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
40 | //If parent is already available, add to thread of parent | 68 | //If parent is already available, add to thread of parent |
41 | if (thread.isEmpty() && parentMessageId.isValid()) { | 69 | if (thread.isEmpty() && parentMessageId.isValid()) { |
42 | thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); | 70 | thread = index().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId); |
diff --git a/common/mailpreprocessor.cpp b/common/mailpreprocessor.cpp index 5c54fbc..253e8b4 100644 --- a/common/mailpreprocessor.cpp +++ b/common/mailpreprocessor.cpp | |||
@@ -104,9 +104,6 @@ static void updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, KMime: | |||
104 | mail.setExtractedBcc(getContactList(msg->bcc(true))); | 104 | mail.setExtractedBcc(getContactList(msg->bcc(true))); |
105 | mail.setExtractedDate(msg->date(true)->dateTime()); | 105 | mail.setExtractedDate(msg->date(true)->dateTime()); |
106 | 106 | ||
107 | //The rest should never change, unless we didn't have the headers available initially. | ||
108 | auto messageId = msg->messageID(true)->identifier(); | ||
109 | |||
110 | //Ensure the mssageId is unique. | 107 | //Ensure the mssageId is unique. |
111 | //If there already is one with the same id we'd have to assign a new message id, which probably doesn't make any sense. | 108 | //If there already is one with the same id we'd have to assign a new message id, which probably doesn't make any sense. |
112 | 109 | ||
@@ -125,12 +122,21 @@ static void updatedIndexedProperties(Sink::ApplicationDomain::Mail &mail, KMime: | |||
125 | parentMessageId = inReplyTo.first(); | 122 | parentMessageId = inReplyTo.first(); |
126 | } | 123 | } |
127 | } | 124 | } |
125 | |||
126 | //The rest should never change, unless we didn't have the headers available initially. | ||
127 | auto messageId = msg->messageID(true)->identifier(); | ||
128 | if (messageId.isEmpty()) { | 128 | if (messageId.isEmpty()) { |
129 | auto tmp = KMime::Message::Ptr::create(); | 129 | //reuse an existing messageis (on modification) |
130 | auto header = tmp->messageID(true); | 130 | auto existing = mail.getMessageId(); |
131 | header->generate("kube.kde.org"); | 131 | if (existing.isEmpty()) { |
132 | messageId = header->as7BitString(); | 132 | auto tmp = KMime::Message::Ptr::create(); |
133 | SinkWarning() << "Message id is empty, generating one: " << messageId; | 133 | auto header = tmp->messageID(true); |
134 | header->generate("kube.kde.org"); | ||
135 | messageId = header->as7BitString(); | ||
136 | SinkWarning() << "Message id is empty, generating one: " << messageId; | ||
137 | } else { | ||
138 | messageId = existing; | ||
139 | } | ||
134 | } | 140 | } |
135 | 141 | ||
136 | mail.setExtractedMessageId(messageId); | 142 | mail.setExtractedMessageId(messageId); |
diff --git a/common/modelresult.cpp b/common/modelresult.cpp index 58703ab..95f4643 100644 --- a/common/modelresult.cpp +++ b/common/modelresult.cpp | |||
@@ -298,16 +298,16 @@ void ModelResult<T, Ptr>::add(const Ptr &value) | |||
298 | auto parent = createIndexFromId(id); | 298 | auto parent = createIndexFromId(id); |
299 | SinkTraceCtx(mLogCtx) << "Added entity " << childId << "id: " << value->identifier() << "parent: " << id; | 299 | SinkTraceCtx(mLogCtx) << "Added entity " << childId << "id: " << value->identifier() << "parent: " << id; |
300 | const auto keys = mTree[id]; | 300 | const auto keys = mTree[id]; |
301 | int index = 0; | 301 | int idx = 0; |
302 | for (; index < keys.size(); index++) { | 302 | for (; idx < keys.size(); idx++) { |
303 | if (childId < keys.at(index)) { | 303 | if (childId < keys.at(idx)) { |
304 | break; | 304 | break; |
305 | } | 305 | } |
306 | } | 306 | } |
307 | // SinkTraceCtx(mLogCtx) << "Inserting rows " << index << parent; | 307 | // SinkTraceCtx(mLogCtx) << "Inserting rows " << index << parent; |
308 | beginInsertRows(parent, index, index); | 308 | beginInsertRows(parent, idx, idx); |
309 | mEntities.insert(childId, value); | 309 | mEntities.insert(childId, value); |
310 | mTree[id].insert(index, childId); | 310 | mTree[id].insert(idx, childId); |
311 | mParents.insert(childId, id); | 311 | mParents.insert(childId, id); |
312 | endInsertRows(); | 312 | endInsertRows(); |
313 | // SinkTraceCtx(mLogCtx) << "Inserted rows " << mTree[id].size(); | 313 | // SinkTraceCtx(mLogCtx) << "Inserted rows " << mTree[id].size(); |
diff --git a/common/notification.cpp b/common/notification.cpp index da31e20..20bc654 100644 --- a/common/notification.cpp +++ b/common/notification.cpp | |||
@@ -48,7 +48,7 @@ static QByteArray name(int type) | |||
48 | 48 | ||
49 | QDebug operator<<(QDebug dbg, const Sink::Notification &n) | 49 | QDebug operator<<(QDebug dbg, const Sink::Notification &n) |
50 | { | 50 | { |
51 | dbg << "Notification(Type:" << name(n.type) << "Id, :" << n.id << ", Code:"; | 51 | dbg << "Notification(Type:" << name(n.type) << ", Id:" << n.id << ", Code:"; |
52 | dbg << n.code; | 52 | dbg << n.code; |
53 | dbg << ", Message:" << n.message << ", Entities:" << n.entities << ")"; | 53 | dbg << ", Message:" << n.message << ", Entities:" << n.entities << ")"; |
54 | return dbg.space(); | 54 | return dbg.space(); |
diff --git a/common/notifier.cpp b/common/notifier.cpp index 1af65e9..1b7cbdb 100644 --- a/common/notifier.cpp +++ b/common/notifier.cpp | |||
@@ -49,6 +49,7 @@ public: | |||
49 | 49 | ||
50 | QList<QSharedPointer<ResourceAccess>> resourceAccess; | 50 | QList<QSharedPointer<ResourceAccess>> resourceAccess; |
51 | QList<std::function<void(const Notification &)>> handler; | 51 | QList<std::function<void(const Notification &)>> handler; |
52 | QSharedPointer<Sink::ResultEmitter<QSharedPointer<Sink::ApplicationDomain::SinkResource> > > mResourceEmitter; | ||
52 | QObject context; | 53 | QObject context; |
53 | }; | 54 | }; |
54 | 55 | ||
@@ -91,6 +92,9 @@ Notifier::Notifier(const Sink::Query &resourceQuery) : d(new Sink::Notifier::Pri | |||
91 | SinkTraceCtx(resourceCtx) << "Resource query complete"; | 92 | SinkTraceCtx(resourceCtx) << "Resource query complete"; |
92 | }); | 93 | }); |
93 | emitter->fetch({}); | 94 | emitter->fetch({}); |
95 | if (resourceQuery.liveQuery()) { | ||
96 | d->mResourceEmitter = emitter; | ||
97 | } | ||
94 | result.first.exec(); | 98 | result.first.exec(); |
95 | } | 99 | } |
96 | 100 | ||
diff --git a/common/resource.cpp b/common/resource.cpp index 32a92ca..804f0bf 100644 --- a/common/resource.cpp +++ b/common/resource.cpp | |||
@@ -56,10 +56,10 @@ class ResourceFactory::Private | |||
56 | { | 56 | { |
57 | public: | 57 | public: |
58 | QByteArrayList capabilities; | 58 | QByteArrayList capabilities; |
59 | static QHash<QString, QPointer<ResourceFactory>> s_loadedFactories; | ||
60 | }; | 59 | }; |
61 | 60 | ||
62 | QHash<QString, QPointer<ResourceFactory>> ResourceFactory::Private::s_loadedFactories; | 61 | typedef QHash<QString, QPointer<ResourceFactory>> FactoryRegistry; |
62 | Q_GLOBAL_STATIC(FactoryRegistry, s_loadedFactories); | ||
63 | 63 | ||
64 | ResourceFactory::ResourceFactory(QObject *parent, const QByteArrayList &capabilities) : QObject(parent), d(new ResourceFactory::Private) | 64 | ResourceFactory::ResourceFactory(QObject *parent, const QByteArrayList &capabilities) : QObject(parent), d(new ResourceFactory::Private) |
65 | { | 65 | { |
@@ -73,7 +73,7 @@ ResourceFactory::~ResourceFactory() | |||
73 | 73 | ||
74 | ResourceFactory *ResourceFactory::load(const QByteArray &resourceName) | 74 | ResourceFactory *ResourceFactory::load(const QByteArray &resourceName) |
75 | { | 75 | { |
76 | ResourceFactory *factory = Private::s_loadedFactories.value(resourceName); | 76 | ResourceFactory *factory = s_loadedFactories->value(resourceName); |
77 | if (factory) { | 77 | if (factory) { |
78 | return factory; | 78 | return factory; |
79 | } | 79 | } |
@@ -96,7 +96,7 @@ ResourceFactory *ResourceFactory::load(const QByteArray &resourceName) | |||
96 | if (object) { | 96 | if (object) { |
97 | factory = qobject_cast<ResourceFactory *>(object); | 97 | factory = qobject_cast<ResourceFactory *>(object); |
98 | if (factory) { | 98 | if (factory) { |
99 | Private::s_loadedFactories.insert(resourceName, factory); | 99 | s_loadedFactories->insert(resourceName, factory); |
100 | //TODO: Instead of always loading both facades and adaptorfactories into the respective singletons, we could also leave this up to the caller. (ResourceFactory::loadFacades(...)) | 100 | //TODO: Instead of always loading both facades and adaptorfactories into the respective singletons, we could also leave this up to the caller. (ResourceFactory::loadFacades(...)) |
101 | factory->registerFacades(resourceName, FacadeFactory::instance()); | 101 | factory->registerFacades(resourceName, FacadeFactory::instance()); |
102 | factory->registerAdaptorFactories(resourceName, AdaptorFactoryRegistry::instance()); | 102 | factory->registerAdaptorFactories(resourceName, AdaptorFactoryRegistry::instance()); |
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp index 808d892..35fa46c 100644 --- a/common/resourceaccess.cpp +++ b/common/resourceaccess.cpp | |||
@@ -232,7 +232,7 @@ KAsync::Job<void> ResourceAccess::Private::initializeSocket() | |||
232 | ResourceAccess::ResourceAccess(const QByteArray &resourceInstanceIdentifier, const QByteArray &resourceType) | 232 | ResourceAccess::ResourceAccess(const QByteArray &resourceInstanceIdentifier, const QByteArray &resourceType) |
233 | : ResourceAccessInterface(), d(new Private(resourceType, resourceInstanceIdentifier, this)) | 233 | : ResourceAccessInterface(), d(new Private(resourceType, resourceInstanceIdentifier, this)) |
234 | { | 234 | { |
235 | mResourceStatus = Sink::ApplicationDomain::OfflineStatus; | 235 | mResourceStatus = Sink::ApplicationDomain::NoStatus; |
236 | SinkTrace() << "Starting access"; | 236 | SinkTrace() << "Starting access"; |
237 | } | 237 | } |
238 | 238 | ||
diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp index dab6aed..0687bbc 100644 --- a/common/resourcefacade.cpp +++ b/common/resourcefacade.cpp | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "store.h" | 24 | #include "store.h" |
25 | #include "resourceaccess.h" | 25 | #include "resourceaccess.h" |
26 | #include "resource.h" | 26 | #include "resource.h" |
27 | #include "facadefactory.h" | ||
27 | 28 | ||
28 | using namespace Sink; | 29 | using namespace Sink; |
29 | 30 | ||
@@ -358,27 +359,50 @@ QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename ApplicationDomain | |||
358 | auto ctx = parentCtx.subContext("accounts"); | 359 | auto ctx = parentCtx.subContext("accounts"); |
359 | auto runner = new LocalStorageQueryRunner<ApplicationDomain::SinkAccount>(query, mIdentifier, mTypeName, sConfigNotifier, ctx); | 360 | auto runner = new LocalStorageQueryRunner<ApplicationDomain::SinkAccount>(query, mIdentifier, mTypeName, sConfigNotifier, ctx); |
360 | auto monitoredResources = QSharedPointer<QSet<QByteArray>>::create(); | 361 | auto monitoredResources = QSharedPointer<QSet<QByteArray>>::create(); |
361 | runner->setStatusUpdater([runner, monitoredResources, ctx](ApplicationDomain::SinkAccount &account) { | 362 | auto monitorResource = [monitoredResources, runner, ctx] (const QByteArray &accountIdentifier, const ApplicationDomain::SinkResource &resource, const ResourceAccess::Ptr &resourceAccess) { |
362 | Query query; | 363 | if (!monitoredResources->contains(resource.identifier())) { |
364 | auto ret = QObject::connect(resourceAccess.data(), &ResourceAccess::notification, runner->guard(), [resource, runner, resourceAccess, accountIdentifier, ctx](const Notification ¬ification) { | ||
365 | SinkTraceCtx(ctx) << "Received notification in facade: " << notification.type; | ||
366 | if (notification.type == Notification::Status) { | ||
367 | runner->statusChanged(accountIdentifier); | ||
368 | } | ||
369 | }); | ||
370 | Q_ASSERT(ret); | ||
371 | monitoredResources->insert(resource.identifier()); | ||
372 | } | ||
373 | }; | ||
374 | runner->setStatusUpdater([this, runner, monitoredResources, ctx, monitorResource](ApplicationDomain::SinkAccount &account) { | ||
375 | Query query{Query::LiveQuery}; | ||
363 | query.filter<ApplicationDomain::SinkResource::Account>(account.identifier()); | 376 | query.filter<ApplicationDomain::SinkResource::Account>(account.identifier()); |
364 | query.request<ApplicationDomain::SinkResource::Account>() | 377 | query.request<ApplicationDomain::SinkResource::Account>() |
365 | .request<ApplicationDomain::SinkResource::Capabilities>(); | 378 | .request<ApplicationDomain::SinkResource::Capabilities>(); |
366 | const auto resources = Store::read<ApplicationDomain::SinkResource>(query); | 379 | const auto resources = Store::read<ApplicationDomain::SinkResource>(query); |
367 | SinkTraceCtx(ctx) << "Found resource belonging to the account " << account.identifier() << " : " << resources; | 380 | SinkTraceCtx(ctx) << "Found resource belonging to the account " << account.identifier() << " : " << resources; |
368 | auto accountIdentifier = account.identifier(); | 381 | auto accountIdentifier = account.identifier(); |
382 | |||
383 | //Monitor for new resources so they can be monitored as well | ||
384 | if (!runner->mResourceEmitter.contains(accountIdentifier)) { | ||
385 | auto facade = Sink::FacadeFactory::instance().getFacade<ApplicationDomain::SinkResource>(); | ||
386 | Q_ASSERT(facade); | ||
387 | |||
388 | auto emitter = facade->load(query, ctx).second; | ||
389 | emitter->onAdded([=](const ApplicationDomain::SinkResource::Ptr &resource) { | ||
390 | auto resourceAccess = Sink::ResourceAccessFactory::instance().getAccess(resource->identifier(), ResourceConfig::getResourceType(resource->identifier())); | ||
391 | monitorResource(accountIdentifier, *resource, resourceAccess); | ||
392 | }); | ||
393 | emitter->onModified([](const ApplicationDomain::SinkResource::Ptr &) {}); | ||
394 | emitter->onRemoved([](const ApplicationDomain::SinkResource::Ptr &) {}); | ||
395 | emitter->onInitialResultSetComplete([](const ApplicationDomain::SinkResource::Ptr &, bool) {}); | ||
396 | emitter->onComplete([]() {}); | ||
397 | emitter->fetch({}); | ||
398 | runner->mResourceEmitter[accountIdentifier] = emitter; | ||
399 | } | ||
400 | |||
369 | QList<int> states; | 401 | QList<int> states; |
402 | //Gather all resources and ensure they are monitored | ||
370 | for (const auto &resource : resources) { | 403 | for (const auto &resource : resources) { |
371 | auto resourceAccess = ResourceAccessFactory::instance().getAccess(resource.identifier(), ResourceConfig::getResourceType(resource.identifier())); | 404 | auto resourceAccess = ResourceAccessFactory::instance().getAccess(resource.identifier(), ResourceConfig::getResourceType(resource.identifier())); |
372 | if (!monitoredResources->contains(resource.identifier())) { | 405 | monitorResource(accountIdentifier, resource, resourceAccess); |
373 | auto ret = QObject::connect(resourceAccess.data(), &ResourceAccess::notification, runner->guard(), [resource, runner, resourceAccess, accountIdentifier, ctx](const Notification ¬ification) { | ||
374 | SinkTraceCtx(ctx) << "Received notification in facade: " << notification.type; | ||
375 | if (notification.type == Notification::Status) { | ||
376 | runner->statusChanged(accountIdentifier); | ||
377 | } | ||
378 | }); | ||
379 | Q_ASSERT(ret); | ||
380 | monitoredResources->insert(resource.identifier()); | ||
381 | } | ||
382 | states << resourceAccess->getResourceStatus(); | 406 | states << resourceAccess->getResourceStatus(); |
383 | } | 407 | } |
384 | const auto status = [&] { | 408 | const auto status = [&] { |
@@ -391,7 +415,10 @@ QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename ApplicationDomain | |||
391 | if (states.contains(ApplicationDomain::OfflineStatus)) { | 415 | if (states.contains(ApplicationDomain::OfflineStatus)) { |
392 | return ApplicationDomain::OfflineStatus; | 416 | return ApplicationDomain::OfflineStatus; |
393 | } | 417 | } |
394 | return ApplicationDomain::ConnectedStatus; | 418 | if (states.contains(ApplicationDomain::ConnectedStatus)) { |
419 | return ApplicationDomain::ConnectedStatus; | ||
420 | } | ||
421 | return ApplicationDomain::NoStatus; | ||
395 | }(); | 422 | }(); |
396 | account.setStatusStatus(status); | 423 | account.setStatusStatus(status); |
397 | }); | 424 | }); |
diff --git a/common/resourcefacade.h b/common/resourcefacade.h index 76fadce..36049c4 100644 --- a/common/resourcefacade.h +++ b/common/resourcefacade.h | |||
@@ -65,6 +65,7 @@ public: | |||
65 | void setStatusUpdater(const std::function<void(DomainType &)> &); | 65 | void setStatusUpdater(const std::function<void(DomainType &)> &); |
66 | void statusChanged(const QByteArray &identifier); | 66 | void statusChanged(const QByteArray &identifier); |
67 | QObject *guard() const; | 67 | QObject *guard() const; |
68 | QMap<QByteArray, QSharedPointer<Sink::ResultEmitter<QSharedPointer<Sink::ApplicationDomain::SinkResource> > > > mResourceEmitter; | ||
68 | 69 | ||
69 | private: | 70 | private: |
70 | void updateStatus(DomainType &entity); | 71 | void updateStatus(DomainType &entity); |
diff --git a/common/storage.h b/common/storage.h index 8c129df..c39b904 100644 --- a/common/storage.h +++ b/common/storage.h | |||
@@ -198,9 +198,9 @@ public: | |||
198 | static QByteArray getTypeFromRevision(const Transaction &, qint64 revision); | 198 | static QByteArray getTypeFromRevision(const Transaction &, qint64 revision); |
199 | static void recordRevision(Transaction &, qint64 revision, const QByteArray &uid, const QByteArray &type); | 199 | static void recordRevision(Transaction &, qint64 revision, const QByteArray &uid, const QByteArray &type); |
200 | static void removeRevision(Transaction &, qint64 revision); | 200 | static void removeRevision(Transaction &, qint64 revision); |
201 | static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid); | 201 | static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type); |
202 | static void removeUid(DataStore::Transaction &transaction, const QByteArray &uid); | 202 | static void removeUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type); |
203 | static void getUids(const Transaction &, const std::function<void(const QByteArray &uid)> &); | 203 | static void getUids(const QByteArray &type, const Transaction &, const std::function<void(const QByteArray &uid)> &); |
204 | 204 | ||
205 | bool exists() const; | 205 | bool exists() const; |
206 | 206 | ||
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index 22e5ae3..5514e31 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp | |||
@@ -267,7 +267,7 @@ bool EntityStore::add(const QByteArray &type, ApplicationDomain::ApplicationDoma | |||
267 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << entity.identifier() << newRevision; }); | 267 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << entity.identifier() << newRevision; }); |
268 | DataStore::setMaxRevision(d->transaction, newRevision); | 268 | DataStore::setMaxRevision(d->transaction, newRevision); |
269 | DataStore::recordRevision(d->transaction, newRevision, entity.identifier(), type); | 269 | DataStore::recordRevision(d->transaction, newRevision, entity.identifier(), type); |
270 | DataStore::recordUid(d->transaction, entity.identifier()); | 270 | DataStore::recordUid(d->transaction, entity.identifier(), type); |
271 | SinkTraceCtx(d->logCtx) << "Wrote entity: " << entity.identifier() << type << newRevision; | 271 | SinkTraceCtx(d->logCtx) << "Wrote entity: " << entity.identifier() << type << newRevision; |
272 | return true; | 272 | return true; |
273 | } | 273 | } |
@@ -379,7 +379,7 @@ bool EntityStore::remove(const QByteArray &type, const Sink::ApplicationDomain:: | |||
379 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << uid << newRevision; }); | 379 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << uid << newRevision; }); |
380 | DataStore::setMaxRevision(d->transaction, newRevision); | 380 | DataStore::setMaxRevision(d->transaction, newRevision); |
381 | DataStore::recordRevision(d->transaction, newRevision, uid, type); | 381 | DataStore::recordRevision(d->transaction, newRevision, uid, type); |
382 | DataStore::removeUid(d->transaction, uid); | 382 | DataStore::removeUid(d->transaction, uid, type); |
383 | return true; | 383 | return true; |
384 | } | 384 | } |
385 | 385 | ||
@@ -516,9 +516,8 @@ void EntityStore::readLatest(const QByteArray &type, const QByteArray &uid, cons | |||
516 | { | 516 | { |
517 | auto db = DataStore::mainDatabase(d->getTransaction(), type); | 517 | auto db = DataStore::mainDatabase(d->getTransaction(), type); |
518 | db.findLatest(uid, | 518 | db.findLatest(uid, |
519 | [=](const QByteArray &key, const QByteArray &value) -> bool { | 519 | [=](const QByteArray &key, const QByteArray &value) { |
520 | callback(DataStore::uidFromKey(key), Sink::EntityBuffer(value.data(), value.size())); | 520 | callback(DataStore::uidFromKey(key), Sink::EntityBuffer(value.data(), value.size())); |
521 | return false; | ||
522 | }, | 521 | }, |
523 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during query: " << error.message << uid; }); | 522 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during query: " << error.message << uid; }); |
524 | } | 523 | } |
@@ -639,7 +638,7 @@ ApplicationDomain::ApplicationDomainType EntityStore::readPrevious(const QByteAr | |||
639 | 638 | ||
640 | void EntityStore::readAllUids(const QByteArray &type, const std::function<void(const QByteArray &uid)> callback) | 639 | void EntityStore::readAllUids(const QByteArray &type, const std::function<void(const QByteArray &uid)> callback) |
641 | { | 640 | { |
642 | DataStore::getUids(d->getTransaction(), callback); | 641 | DataStore::getUids(type, d->getTransaction(), callback); |
643 | } | 642 | } |
644 | 643 | ||
645 | bool EntityStore::contains(const QByteArray &type, const QByteArray &uid) | 644 | bool EntityStore::contains(const QByteArray &type, const QByteArray &uid) |
@@ -653,7 +652,7 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) | |||
653 | bool alreadyRemoved = false; | 652 | bool alreadyRemoved = false; |
654 | DataStore::mainDatabase(d->transaction, type) | 653 | DataStore::mainDatabase(d->transaction, type) |
655 | .findLatest(uid, | 654 | .findLatest(uid, |
656 | [&found, &alreadyRemoved](const QByteArray &key, const QByteArray &data) -> bool { | 655 | [&found, &alreadyRemoved](const QByteArray &key, const QByteArray &data) { |
657 | auto entity = GetEntity(data.data()); | 656 | auto entity = GetEntity(data.data()); |
658 | if (entity && entity->metadata()) { | 657 | if (entity && entity->metadata()) { |
659 | auto metadata = GetMetadata(entity->metadata()->Data()); | 658 | auto metadata = GetMetadata(entity->metadata()->Data()); |
@@ -662,7 +661,6 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) | |||
662 | alreadyRemoved = true; | 661 | alreadyRemoved = true; |
663 | } | 662 | } |
664 | } | 663 | } |
665 | return false; | ||
666 | }, | 664 | }, |
667 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; }); | 665 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; }); |
668 | if (!found) { | 666 | if (!found) { |
diff --git a/common/storage_common.cpp b/common/storage_common.cpp index 8603787..630dae9 100644 --- a/common/storage_common.cpp +++ b/common/storage_common.cpp | |||
@@ -156,19 +156,19 @@ void DataStore::removeRevision(DataStore::Transaction &transaction, qint64 revis | |||
156 | transaction.openDatabase("revisionType").remove(QByteArray::number(revision)); | 156 | transaction.openDatabase("revisionType").remove(QByteArray::number(revision)); |
157 | } | 157 | } |
158 | 158 | ||
159 | void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid) | 159 | void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type) |
160 | { | 160 | { |
161 | transaction.openDatabase("uids").write(uid, ""); | 161 | transaction.openDatabase(type + "uids").write(uid, ""); |
162 | } | 162 | } |
163 | 163 | ||
164 | void DataStore::removeUid(DataStore::Transaction &transaction, const QByteArray &uid) | 164 | void DataStore::removeUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type) |
165 | { | 165 | { |
166 | transaction.openDatabase("uids").remove(uid); | 166 | transaction.openDatabase(type + "uids").remove(uid); |
167 | } | 167 | } |
168 | 168 | ||
169 | void DataStore::getUids(const Transaction &transaction, const std::function<void(const QByteArray &uid)> &callback) | 169 | void DataStore::getUids(const QByteArray &type, const Transaction &transaction, const std::function<void(const QByteArray &uid)> &callback) |
170 | { | 170 | { |
171 | transaction.openDatabase("uids").scan("", [&] (const QByteArray &key, const QByteArray &) { | 171 | transaction.openDatabase(type + "uids").scan("", [&] (const QByteArray &key, const QByteArray &) { |
172 | callback(key); | 172 | callback(key); |
173 | return true; | 173 | return true; |
174 | }); | 174 | }); |
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index f7999d1..58e3a9a 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp | |||
@@ -406,7 +406,7 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool | |||
406 | mdb_cursor_close(cursor); | 406 | mdb_cursor_close(cursor); |
407 | 407 | ||
408 | if (rc) { | 408 | if (rc) { |
409 | Error error(d->name.toLatin1() + d->db, getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); | 409 | Error error(d->name.toLatin1() + d->db, getErrorCode(rc), QByteArray("Error during scan. Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); |
410 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | 410 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); |
411 | } | 411 | } |
412 | 412 | ||
@@ -420,6 +420,11 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi | |||
420 | // Not an error. We rely on this to read nothing from non-existing databases. | 420 | // Not an error. We rely on this to read nothing from non-existing databases. |
421 | return; | 421 | return; |
422 | } | 422 | } |
423 | if (k.isEmpty()) { | ||
424 | Error error(d->name.toLatin1() + d->db, GenericError, QByteArray("Can't use findLatest with empty key.")); | ||
425 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
426 | return; | ||
427 | } | ||
423 | 428 | ||
424 | int rc; | 429 | int rc; |
425 | MDB_val key; | 430 | MDB_val key; |
@@ -441,25 +446,23 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi | |||
441 | if ((rc = mdb_cursor_get(cursor, &key, &data, op)) == 0) { | 446 | if ((rc = mdb_cursor_get(cursor, &key, &data, op)) == 0) { |
442 | // The first lookup will find a key that is equal or greather than our key | 447 | // The first lookup will find a key that is equal or greather than our key |
443 | if (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) { | 448 | if (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) { |
444 | bool advanced = false; | 449 | //Read next value until we no longer match |
445 | while (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) { | 450 | while (QByteArray::fromRawData((char *)key.mv_data, key.mv_size).startsWith(k)) { |
446 | advanced = true; | ||
447 | MDB_cursor_op nextOp = MDB_NEXT; | 451 | MDB_cursor_op nextOp = MDB_NEXT; |
448 | rc = mdb_cursor_get(cursor, &key, &data, nextOp); | 452 | rc = mdb_cursor_get(cursor, &key, &data, nextOp); |
449 | if (rc) { | 453 | if (rc) { |
450 | break; | 454 | break; |
451 | } | 455 | } |
452 | } | 456 | } |
453 | if (advanced) { | 457 | //Now read the previous value, and that's the latest one |
454 | MDB_cursor_op prefOp = MDB_PREV; | 458 | MDB_cursor_op prefOp = MDB_PREV; |
455 | // We read past the end above, just take the last value | 459 | // We read past the end above, just take the last value |
456 | if (rc == MDB_NOTFOUND) { | 460 | if (rc == MDB_NOTFOUND) { |
457 | prefOp = MDB_LAST; | 461 | prefOp = MDB_LAST; |
458 | } | ||
459 | rc = mdb_cursor_get(cursor, &key, &data, prefOp); | ||
460 | foundValue = true; | ||
461 | resultHandler(QByteArray::fromRawData((char *)key.mv_data, key.mv_size), QByteArray::fromRawData((char *)data.mv_data, data.mv_size)); | ||
462 | } | 462 | } |
463 | rc = mdb_cursor_get(cursor, &key, &data, prefOp); | ||
464 | foundValue = true; | ||
465 | resultHandler(QByteArray::fromRawData((char *)key.mv_data, key.mv_size), QByteArray::fromRawData((char *)data.mv_data, data.mv_size)); | ||
463 | } | 466 | } |
464 | } | 467 | } |
465 | 468 | ||
@@ -471,10 +474,10 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi | |||
471 | mdb_cursor_close(cursor); | 474 | mdb_cursor_close(cursor); |
472 | 475 | ||
473 | if (rc) { | 476 | if (rc) { |
474 | Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); | 477 | Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Error during find latest. Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); |
475 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | 478 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); |
476 | } else if (!foundValue) { | 479 | } else if (!foundValue) { |
477 | Error error(d->name.toLatin1(), 1, QByteArray("Key: ") + k + " : No value found"); | 480 | Error error(d->name.toLatin1(), 1, QByteArray("Error during find latest. Key: ") + k + " : No value found"); |
478 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | 481 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); |
479 | } | 482 | } |
480 | 483 | ||
diff --git a/common/store.cpp b/common/store.cpp index 4735113..1701a43 100644 --- a/common/store.cpp +++ b/common/store.cpp | |||
@@ -36,10 +36,34 @@ | |||
36 | #include "storage.h" | 36 | #include "storage.h" |
37 | #include "log.h" | 37 | #include "log.h" |
38 | 38 | ||
39 | #define ASSERT_ENUMS_MATCH(A, B) Q_STATIC_ASSERT_X(static_cast<int>(A) == static_cast<int>(B), "The enum values must match"); | ||
40 | |||
41 | //Ensure the copied enum matches | ||
42 | typedef ModelResult<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Mail::Ptr> MailModelResult; | ||
43 | ASSERT_ENUMS_MATCH(Sink::Store::DomainObjectBaseRole, MailModelResult::DomainObjectBaseRole) | ||
44 | ASSERT_ENUMS_MATCH(Sink::Store::ChildrenFetchedRole, MailModelResult::ChildrenFetchedRole) | ||
45 | ASSERT_ENUMS_MATCH(Sink::Store::DomainObjectRole, MailModelResult::DomainObjectRole) | ||
46 | ASSERT_ENUMS_MATCH(Sink::Store::StatusRole, MailModelResult::StatusRole) | ||
47 | ASSERT_ENUMS_MATCH(Sink::Store::WarningRole, MailModelResult::WarningRole) | ||
48 | ASSERT_ENUMS_MATCH(Sink::Store::ProgressRole, MailModelResult::ProgressRole) | ||
49 | |||
39 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) | 50 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResultEmitter<Sink::ApplicationDomain::SinkResource::Ptr>>) |
40 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>); | 51 | Q_DECLARE_METATYPE(QSharedPointer<Sink::ResourceAccessInterface>); |
41 | Q_DECLARE_METATYPE(std::shared_ptr<void>); | 52 | Q_DECLARE_METATYPE(std::shared_ptr<void>); |
42 | 53 | ||
54 | |||
55 | static bool sanityCheckQuery(const Sink::Query &query) | ||
56 | { | ||
57 | for (const auto &id : query.ids()) { | ||
58 | if (id.isEmpty()) { | ||
59 | SinkError() << "Empty id in query."; | ||
60 | return false; | ||
61 | } | ||
62 | } | ||
63 | return true; | ||
64 | } | ||
65 | |||
66 | |||
43 | namespace Sink { | 67 | namespace Sink { |
44 | 68 | ||
45 | QString Store::storageLocation() | 69 | QString Store::storageLocation() |
@@ -138,6 +162,7 @@ static Log::Context getQueryContext(const Sink::Query &query, const QByteArray & | |||
138 | template <class DomainType> | 162 | template <class DomainType> |
139 | QSharedPointer<QAbstractItemModel> Store::loadModel(const Query &query) | 163 | QSharedPointer<QAbstractItemModel> Store::loadModel(const Query &query) |
140 | { | 164 | { |
165 | Q_ASSERT(sanityCheckQuery(query)); | ||
141 | auto ctx = getQueryContext(query, ApplicationDomain::getTypeName<DomainType>()); | 166 | auto ctx = getQueryContext(query, ApplicationDomain::getTypeName<DomainType>()); |
142 | auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr>>::create(query, query.requestedProperties, ctx); | 167 | auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr>>::create(query, query.requestedProperties, ctx); |
143 | 168 | ||
@@ -189,6 +214,10 @@ KAsync::Job<void> Store::create(const DomainType &domainObject) | |||
189 | template <class DomainType> | 214 | template <class DomainType> |
190 | KAsync::Job<void> Store::modify(const DomainType &domainObject) | 215 | KAsync::Job<void> Store::modify(const DomainType &domainObject) |
191 | { | 216 | { |
217 | if (domainObject.changedProperties().isEmpty()) { | ||
218 | SinkLog() << "Nothing to modify: " << domainObject.identifier(); | ||
219 | return KAsync::null(); | ||
220 | } | ||
192 | SinkLog() << "Modify: " << domainObject; | 221 | SinkLog() << "Modify: " << domainObject; |
193 | // Potentially move to separate thread as well | 222 | // Potentially move to separate thread as well |
194 | auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier()); | 223 | auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier()); |
@@ -198,6 +227,10 @@ KAsync::Job<void> Store::modify(const DomainType &domainObject) | |||
198 | template <class DomainType> | 227 | template <class DomainType> |
199 | KAsync::Job<void> Store::modify(const Query &query, const DomainType &domainObject) | 228 | KAsync::Job<void> Store::modify(const Query &query, const DomainType &domainObject) |
200 | { | 229 | { |
230 | if (domainObject.changedProperties().isEmpty()) { | ||
231 | SinkLog() << "Nothing to modify: " << domainObject.identifier(); | ||
232 | return KAsync::null(); | ||
233 | } | ||
201 | SinkLog() << "Modify: " << query << domainObject; | 234 | SinkLog() << "Modify: " << query << domainObject; |
202 | return fetchAll<DomainType>(query) | 235 | return fetchAll<DomainType>(query) |
203 | .each([=] (const typename DomainType::Ptr &entity) { | 236 | .each([=] (const typename DomainType::Ptr &entity) { |
@@ -311,9 +344,14 @@ KAsync::Job<void> Store::synchronize(const Sink::Query &query) | |||
311 | 344 | ||
312 | KAsync::Job<void> Store::synchronize(const Sink::SyncScope &scope) | 345 | KAsync::Job<void> Store::synchronize(const Sink::SyncScope &scope) |
313 | { | 346 | { |
347 | auto resourceFilter = scope.getResourceFilter(); | ||
348 | //Filter resources by type by default | ||
349 | if (!resourceFilter.propertyFilter.contains(ApplicationDomain::SinkResource::Capabilities::name) && !scope.type().isEmpty()) { | ||
350 | resourceFilter.propertyFilter.insert(ApplicationDomain::SinkResource::Capabilities::name, Query::Comparator{scope.type(), Query::Comparator::Contains}); | ||
351 | } | ||
314 | Sink::Query query; | 352 | Sink::Query query; |
315 | query.setFilter(scope.getResourceFilter()); | 353 | query.setFilter(resourceFilter); |
316 | SinkLog() << "Synchronizing: " << query; | 354 | SinkLog() << "Synchronizing all resource matching: " << query; |
317 | return fetchAll<ApplicationDomain::SinkResource>(query) | 355 | return fetchAll<ApplicationDomain::SinkResource>(query) |
318 | .template each([scope](const ApplicationDomain::SinkResource::Ptr &resource) -> KAsync::Job<void> { | 356 | .template each([scope](const ApplicationDomain::SinkResource::Ptr &resource) -> KAsync::Job<void> { |
319 | return synchronize(resource->identifier(), scope); | 357 | return synchronize(resource->identifier(), scope); |
@@ -337,6 +375,7 @@ KAsync::Job<QList<typename DomainType::Ptr>> Store::fetchAll(const Sink::Query & | |||
337 | template <class DomainType> | 375 | template <class DomainType> |
338 | KAsync::Job<QList<typename DomainType::Ptr>> Store::fetch(const Sink::Query &query, int minimumAmount) | 376 | KAsync::Job<QList<typename DomainType::Ptr>> Store::fetch(const Sink::Query &query, int minimumAmount) |
339 | { | 377 | { |
378 | Q_ASSERT(sanityCheckQuery(query)); | ||
340 | auto model = loadModel<DomainType>(query); | 379 | auto model = loadModel<DomainType>(query); |
341 | auto list = QSharedPointer<QList<typename DomainType::Ptr>>::create(); | 380 | auto list = QSharedPointer<QList<typename DomainType::Ptr>>::create(); |
342 | auto context = QSharedPointer<QObject>::create(); | 381 | auto context = QSharedPointer<QObject>::create(); |
@@ -388,6 +427,7 @@ DomainType Store::readOne(const Sink::Query &query) | |||
388 | template <class DomainType> | 427 | template <class DomainType> |
389 | QList<DomainType> Store::read(const Sink::Query &query_) | 428 | QList<DomainType> Store::read(const Sink::Query &query_) |
390 | { | 429 | { |
430 | Q_ASSERT(sanityCheckQuery(query_)); | ||
391 | auto query = query_; | 431 | auto query = query_; |
392 | query.setFlags(Query::SynchronousQuery); | 432 | query.setFlags(Query::SynchronousQuery); |
393 | 433 | ||
diff --git a/common/store.h b/common/store.h index 34e14df..3ad547e 100644 --- a/common/store.h +++ b/common/store.h | |||
@@ -75,12 +75,15 @@ KAsync::Job<void> SINK_EXPORT create(const DomainType &domainObject); | |||
75 | * Modify an entity. | 75 | * Modify an entity. |
76 | * | 76 | * |
77 | * This includes moving etc. since these are also simple settings on a property. | 77 | * This includes moving etc. since these are also simple settings on a property. |
78 | * Note that the modification will be dropped if there is no changedProperty on the domain object. | ||
78 | */ | 79 | */ |
79 | template <class DomainType> | 80 | template <class DomainType> |
80 | KAsync::Job<void> SINK_EXPORT modify(const DomainType &domainObject); | 81 | KAsync::Job<void> SINK_EXPORT modify(const DomainType &domainObject); |
81 | 82 | ||
82 | /** | 83 | /** |
83 | * Modify a set of entities identified by @param query. | 84 | * Modify a set of entities identified by @param query. |
85 | * | ||
86 | * Note that the modification will be dropped if there is no changedProperty on the domain object. | ||
84 | */ | 87 | */ |
85 | template <class DomainType> | 88 | template <class DomainType> |
86 | KAsync::Job<void> SINK_EXPORT modify(const Query &query, const DomainType &domainObject); | 89 | KAsync::Job<void> SINK_EXPORT modify(const Query &query, const DomainType &domainObject); |
diff --git a/common/synchronizer.cpp b/common/synchronizer.cpp index 3b32e68..46d3980 100644 --- a/common/synchronizer.cpp +++ b/common/synchronizer.cpp | |||
@@ -40,7 +40,7 @@ Synchronizer::Synchronizer(const Sink::ResourceContext &context) | |||
40 | mSyncStorage(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::DataStore::ReadWrite), | 40 | mSyncStorage(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::DataStore::ReadWrite), |
41 | mSyncInProgress(false) | 41 | mSyncInProgress(false) |
42 | { | 42 | { |
43 | mCurrentState.push(ApplicationDomain::Status::OfflineStatus); | 43 | mCurrentState.push(ApplicationDomain::Status::NoStatus); |
44 | SinkTraceCtx(mLogCtx) << "Starting synchronizer: " << mResourceContext.resourceType << mResourceContext.instanceId(); | 44 | SinkTraceCtx(mLogCtx) << "Starting synchronizer: " << mResourceContext.resourceType << mResourceContext.instanceId(); |
45 | } | 45 | } |
46 | 46 | ||
@@ -344,8 +344,11 @@ void Synchronizer::setStatusFromResult(const KAsync::Error &error, const QString | |||
344 | } else if (error.errorCode == ApplicationDomain::LoginError) { | 344 | } else if (error.errorCode == ApplicationDomain::LoginError) { |
345 | //If we failed to login altough we could connect that indicates a problem with our setup. | 345 | //If we failed to login altough we could connect that indicates a problem with our setup. |
346 | setStatus(ApplicationDomain::ErrorStatus, s, requestId); | 346 | setStatus(ApplicationDomain::ErrorStatus, s, requestId); |
347 | } else if (error.errorCode == ApplicationDomain::ConnectionLostError) { | ||
348 | //We've lost the connection so we assume the connection to the server broke. | ||
349 | setStatus(ApplicationDomain::OfflineStatus, s, requestId); | ||
347 | } | 350 | } |
348 | //We don't know what kind of error this was, so we assume it's transient and don't change ou status. | 351 | //We don't know what kind of error this was, so we assume it's transient and don't change our status. |
349 | } else { | 352 | } else { |
350 | //An operation against the server worked, so we're probably online. | 353 | //An operation against the server worked, so we're probably online. |
351 | setStatus(ApplicationDomain::ConnectedStatus, s, requestId); | 354 | setStatus(ApplicationDomain::ConnectedStatus, s, requestId); |
@@ -593,17 +596,13 @@ KAsync::Job<void> Synchronizer::replay(const QByteArray &type, const QByteArray | |||
593 | KAsync::Job<QByteArray> job = KAsync::null<QByteArray>(); | 596 | KAsync::Job<QByteArray> job = KAsync::null<QByteArray>(); |
594 | //TODO This requires supporting every domain type here as well. Can we solve this better so we can do the dispatch somewhere centrally? | 597 | //TODO This requires supporting every domain type here as well. Can we solve this better so we can do the dispatch somewhere centrally? |
595 | if (type == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) { | 598 | if (type == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) { |
596 | auto folder = store().readEntity<ApplicationDomain::Folder>(key); | 599 | job = replay(store().readEntity<ApplicationDomain::Folder>(key), operation, oldRemoteId, modifiedProperties); |
597 | job = replay(folder, operation, oldRemoteId, modifiedProperties); | ||
598 | } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) { | 600 | } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) { |
599 | auto mail = store().readEntity<ApplicationDomain::Mail>(key); | 601 | job = replay(store().readEntity<ApplicationDomain::Mail>(key), operation, oldRemoteId, modifiedProperties); |
600 | job = replay(mail, operation, oldRemoteId, modifiedProperties); | ||
601 | } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { | 602 | } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { |
602 | auto mail = store().readEntity<ApplicationDomain::Contact>(key); | 603 | job = replay(store().readEntity<ApplicationDomain::Contact>(key), operation, oldRemoteId, modifiedProperties); |
603 | job = replay(mail, operation, oldRemoteId, modifiedProperties); | ||
604 | } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) { | 604 | } else if (type == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) { |
605 | auto mail = store().readEntity<ApplicationDomain::Addressbook>(key); | 605 | job = replay(store().readEntity<ApplicationDomain::Addressbook>(key), operation, oldRemoteId, modifiedProperties); |
606 | job = replay(mail, operation, oldRemoteId, modifiedProperties); | ||
607 | } else { | 606 | } else { |
608 | SinkErrorCtx(mLogCtx) << "Replayed unknown type: " << type; | 607 | SinkErrorCtx(mLogCtx) << "Replayed unknown type: " << type; |
609 | } | 608 | } |